The 15th Zhejiang University Programming Contest - Onsite | ||
---|---|---|
A | Find the Spy | 48.59% (104/214) |
B | Valid Pattern Lock | 18.59% (66/355) |
C | Intersection | 15.55% (7/45) |
D | Paths on the Tree | 0.00% (0/15) |
E | Quiz for EXO-L | 18.60% (8/43) |
F | Superbot | 26.12% (29/111) |
G | Cylinder Candy | 57.14% (36/63) |
H | Earthstone: Easy Version | 53.92% (103/191) |
I | GCD Expectation | 7.14% (1/14) |
上面的是现场过题的情况.
The 15th Zhejiang University Programming Contest - Onilne | ||
---|---|---|
A | Find the Spy | 63.48% (433/682) |
B | Valid Pattern Lock | 20.06% (197/982) |
C | Intersection | 32.60% (15/46) |
D | Paths on the Tree | 0.00% (0/5) |
E | Quiz for EXO-L | 2.99% (7/234) |
F | Superbot | 28.09% (93/331) |
G | Cylinder Candy | 32.64% (63/193) |
H | Earthstone: Easy Version | 45.75% (383/837) |
I | GCD Expectation | 26.47% (27/102) |
这个是同步赛过题的情况. D题没有人过, I题挺old的, 同步赛过了一大片人, 现场却没几个人过, 其他题目都还好.
给出$n$个数, 只有一个是不同的, 找出那个数.
数据规模: $3 \le n \le 100$
应该没人不会做这题吧.
#include <bits/stdc++.h> using namespace std; int main() { int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { int n; scanf("%d", &n); vector<int> A(n); for (int i = 0; i < n; ++ i) scanf("%d", &A[i]); sort(A.begin(), A.end()); if (A[0] == A[1]) printf("%d\n", A.back()); else printf("%d\n", A[0]); } return 0; }
给出屏幕锁中$n$个点, 输出所有的pattern lock.
数据规模: $1 \le n \le 9$
应该没人不会做这题吧.
#include <bits/stdc++.h> using namespace std; typedef vector<int> VI; const int cor[][2] = { {0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}, {2, 0}, {2, 1}, {2, 2} }; bool check(int a[], int n) { static bool vis[3][3]; memset(vis, 0, sizeof(vis)); int sx = cor[a[0]][0], sy = cor[a[0]][1]; vis[sx][sy] = true; for (int i = 1; i < n; ++ i) { int ex = cor[a[i]][0], ey = cor[a[i]][1]; int dx = ex - sx, dy = ey - sy, g = __gcd(abs(dx), abs(dy)); dx /= g; dy /= g; while (sx != ex || sy != ey) { sx += dx; sy += dy; if ((sx != ex || sy != ey) && !vis[sx][sy]) return false; vis[sx][sy] = true; } } return true; } int main() { int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { int n, a[10]; scanf("%d", &n); assert(3 <= n && n <= 9); for (int i = 0; i < n; ++ i) { scanf("%d", a + i); -- a[i]; assert(0 <= a[i] && a[i] <= 8); } sort(a, a + n); for (int i = 1; i < n; ++ i) assert(a[i] != a[i - 1]); vector<VI> ret; do { if (check(a, n)) ret.push_back(VI(a, a + n)); } while (next_permutation(a, a + n)); printf("%d\n", (int)ret.size()); for (auto &s : ret) { for (int i = 0; i < n; ++ i) { printf("%d%c", s[i] + 1, " \n"[i == n - 1]); } } } return 0; }
给出$n$条线段, 每次可以选择两个点交换他们的坐标. 要求一个不超过$n+10$步的操作序列, 使得最终没有线段相交.
数据规模: $1 \le n \le 10^5$
我们只要确定了最终每个点的位置之后交换肯定最多需要$n$次. 然后题目有若干限制, 我们可以随便搞.
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> PII; const int MAXN = 200000 + 10; struct Point { int x, y; Point() {} Point(int a, int b): x(a), y(b) {} bool operator < (const Point &rhs) const { return x < rhs.x || (x == rhs.x && y < rhs.y); } bool operator == (const Point &rhs) const { return x == rhs.x && y == rhs.y; } Point operator + (const Point &rhs) const { return Point(x + rhs.x, y + rhs.y); } Point operator - (const Point &rhs) const { return Point(x - rhs.x, y - rhs.y); } LL dot(const Point &rhs) const { return (LL)x * rhs.x + (LL)y * rhs.y; } LL det(const Point &rhs) const { return (LL)x * rhs.y - (LL)y * rhs.x; } LL sqrlen() const { return (LL)x * x + (LL)y * y; } } P[MAXN], Q[MAXN]; map<Point, int> mp; map<LL, vector<Point> > layer; vector<PII> ret; int op[MAXN], N; void change(int a, int b) { ret.push_back(PII(a, b)); swap(P[a], P[b]); mp[P[a]] = a; mp[P[b]] = b; } void solve() { ret.clear(); sort(Q, Q + 2 * N); for (int i = 0; i < N * 2; i += 2) { Point &A = Q[i], &B = Q[i + 1]; int a = mp[A], b = mp[B]; if (op[a] == b) continue; change(op[a], b); a = mp[A], b = mp[B]; } } int main() { int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { scanf("%d", &N); mp.clear(); for (int i = 0; i < 2 * N; ++ i) { scanf("%d%d", &P[i].x, &P[i].y); Q[i] = P[i]; mp[P[i]] = i; } for (int i = 0; i < N; ++ i) { int a, b; scanf("%d%d", &a, &b); -- a, -- b; op[a] = b; op[b] = a; } solve(); printf("%d\n", (int)ret.size()); for (auto &v : ret) { printf("%d %d\n", v.first + 1, v.second + 1); } } return 0; }
给出一棵$n$个节点的树, 问有多少路径对, 公共点不超过$k$个.
数据规模: $1 \le n, k \le 88888$
有两种方法, 树分治和启发式合并. 可以直接做也可以考虑补集. 下面考虑直接做的方法
对于每个重心, 有三种情况:
对于每个根节点, 需要考虑两种情况
然后考虑清楚就好了, 具体讲起来实在麻烦, 还是去看代码吧.
公共节点数目是0或者1的时候, 直接搞不好算, 还是推荐利用补集的方法.
#include <bits/stdc++.h> using namespace std; typedef unsigned long long LL; typedef pair<int, LL> PII; const int MAXN = 100000 + 10; struct Edge { int v, nx; Edge() {} Edge(int a, int b): v(a), nx(b) {} } E[MAXN << 1]; int G[MAXN], sz[MAXN], dep[MAXN]; bool vis[MAXN]; LL sz2[MAXN]; int rt, mins, total; int N, K; LL sqr(LL x) {return x * x;} struct BIT { LL u[MAXN]; int n; void init(int n) { this->n = n; for (int i = 0; i <= n; ++ i) u[i] = 0; } void add(int x, LL v) { for (; x <= n; x += ~x & x + 1) u[x] += v; } LL get(int x) { LL r = 0; x = min(x, n); for (; x >= 0; x -= ~x & x + 1) r += u[x]; return r; } } zpt; void getSize(int u, int f = -1) { sz[u] = 1; sz2[u] = 0; int mx = 0; dep[u] = 0; for (int it = G[u]; ~it; it = E[it].nx) { int v = E[it].v; if (vis[v] || v == f) continue; getSize(v, u); sz[u] += sz[v]; mx = max(mx, sz[v]); sz2[u] += sqr(sz[v]); dep[u] = max(dep[u], dep[v] + 1); } mx = max(mx, total - sz[u]); if (mx < mins) mins = mx, rt = u; } LL S[MAXN], ps1, ps2; vector<PII> entry; LL count(int u, int f, int d) { LL ret = 0, coef = sqr(sz[u]) - sz2[u]; ret = coef * zpt.get(K - d - 1); entry.push_back(PII(d, coef)); if (d + 1 <= K) ret += ps2 * coef; if (d >= 2) ret += ps1 * coef * (S[d - 1] - S[max(0, d - K)]) * 2; for (int it = G[u]; ~it; it = E[it].nx) { int v = E[it].v; if (vis[v] || v == f) continue; S[d] = S[d - 1] + sz[u] - sz[v]; ret += count(v, u, d + 1); } return ret; } LL solve(int u, int tot) { LL ret = 0; mins = N * 2, total = tot; getSize(u); u = rt; vis[u] = true; getSize(u); zpt.init(dep[u]); S[0] = 0; for (int it = G[u]; ~it; it = E[it].nx) { int v = E[it].v; if (vis[v]) continue; ps1 = sz[u] - sz[v]; ps2 = sqr(ps1) - sz2[u] + sqr(sz[v]); entry.clear(); ret += count(v, u, 1); for (auto &e : entry) zpt.add(e.first, e.second); } for (int it = G[u]; ~it; it = E[it].nx) { int v = E[it].v; if (vis[v]) continue; ret += solve(v, sz[v]); } return ret; } int main() { int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { scanf("%d%d", &N, &K); int sz = 0; for (int i = 0; i < N; ++ i) G[i] = -1; for (int i = 1; i < N; ++ i) { int u, v; scanf("%d%d", &u, &v); -- u; -- v; E[sz] = Edge(v, G[u]); G[u] = sz ++; E[sz] = Edge(u, G[v]); G[v] = sz ++; } for (int i = 0; i < N; ++ i) vis[i] = false; LL ret = solve(0, N); K = N; for (int i = 0; i < N; ++ i) vis[i] = false; LL zero = sqr((LL)N * (N - 1) / 2 + N) - solve(0, N); ret += zero; printf("%llu\n", ret); } return 0; }
#include <bits/stdc++.h> using namespace std; typedef unsigned long long LL; const int MAXN = 100000 + 10; const int INF = ~0U>>1; struct Node { Node *ch[2]; int key, pri, sz; LL sum, val; void update() { sum = val + ch[0]->sum + ch[1]->sum; sz = ch[0]->sz + ch[1]->sz + 1; } } pool[MAXN << 4], *null, *pt; struct Treap { Node *rt; map<int, LL> mp; static Node* NewNode(int key, LL val) { pt->key = key; pt->val = pt->sum = val; pt->ch[0] = pt->ch[1] = null; pt->sz = 1; pt->pri = rand() - 1; return pt ++; } int size() {return rt->sz;} void rot(Node* &o, int d) { Node *k = o->ch[d]; o->ch[d] = k->ch[!d]; k->ch[!d] = o; o->update(); k->update(); o = k; } void ins(Node* &o, int key, LL val) { if (o == null) o = NewNode(key, val); else { if (key == o->key) { o->val += val; o->sum += val; return; } int d = (key > o->key); ins(o->ch[d], key, val); if (o->ch[d]->pri < o->pri) rot(o, d); else o->update(); } o->update(); } LL get(Node *o, int k) { int s = o->ch[0]->sz + 1; if (k == s) return o->val + o->ch[0]->sum; else if (k < s) return get(o->ch[0], k); else return o->val + o->ch[0]->sum + get(o->ch[1], k - s); } LL get(int k) { if (size() == 0 || k <= 0) return 0; else return get(rt, min(k, size())); } void ins(int key, LL val) { ins(rt, key, val); mp[key] += val; } void merge(const Treap &rhs) { int now = -size(); for (auto &v : rhs.mp) { ins(now ++, v.second); } } } tp[MAXN]; vector<int> G[MAXN]; LL sz[MAXN], sz2[MAXN]; int N, K; void init() { pt = pool; null = Treap::NewNode(0, 0); null->ch[0] = null->ch[1] = null; null->sz = 0; null->pri = INF; for (int i = 1; i <= N; ++ i) { tp[i].rt = null; tp[i].mp.clear(); } } /*Treap& solve(int u, int K, int f = -1) { sz[u] = 1; sz2[u] = 0; Treap &tu = tp[u]; vector<PIT> pt; for (auto &v : G[u]) if (v != f) { Treap &tv = solve(v, K, u); pt.push_back(PIT(v, tv)); sz[u] += sz[v]; sz2[u] += sqr(sz[v]); } for (size_t i = 0; i < pt.size(); ++ i) { int v = pt[i].first; Treap &tv = pt[i].second; LL coef = sqr(N - sz[v]) - (sz2[u] - sqr(sz[v])) - sqr(N - sz[u]); ret += tv.get(K - 1) * coef; if (tv.size() > tu.size()) swap(tu, tv); int lev = 1; for (auto &x : tv.mp) { ret += tu.get(K - lev - 1) * x.second; ++ lev; } tu.merge(tv); } tu.ins(-(tu.size() + 1), sqr(sz[u]) - sz2[u]); return tu; }*/ typedef pair<int, Treap&> PIT; vector<PIT> cs[MAXN]; inline LL sqr(LL n) {return n * n;} LL solve(int K) { init(); static int Q[MAXN], vis[MAXN], fa[MAXN]; LL ret = 0; for (int i = 1; i <= N; ++ i) { vis[i] = false; sz[i] = sz2[i] = 0; cs[i].clear(); } Q[0] = 1; vis[1] = true; for (int h = 0, t = 1; h < t; ++ h) { int u = Q[h]; for (auto &v : G[u]) if (!vis[v]) { Q[t ++] = v; vis[v] = true; fa[v] = u; } } for (int i = N - 1; i >= 0; -- i) { int u = Q[i]; sz[u] ++; Treap &tu = tp[u]; for (size_t j = 0; j < cs[u].size(); ++ j) { int v = cs[u][j].first; Treap &tv = cs[u][j].second; LL coef = sqr(N - sz[v]) - (sz2[u] - sqr(sz[v])) - sqr(N - sz[u]); ret += tv.get(K - 1) * coef; if (tv.size() > tu.size()) swap(tu, tv); int lev = 1; for (auto &x : tv.mp) { ret += tu.get(K - lev - 1) * x.second; ++ lev; } tu.merge(tv); } sz[fa[u]] += sz[u]; sz2[fa[u]] += sqr(sz[u]); tu.ins(-(tu.size() + 1), sqr(sz[u]) - sz2[u]); cs[fa[u]].push_back(PIT(u, tu)); } return ret; } int main() { int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { scanf("%d%d", &N, &K); for (int i = 1; i <= N; ++ i) G[i].clear(); for (int i = 1; i < N; ++ i) { int u, v; scanf("%d%d", &u, &v); G[u].push_back(v); G[v].push_back(u); } LL ret = sqr((LL)N * (N - 1) / 2 + N) - solve(N); ret += solve(K); printf("%llu\n", ret); } }
给出12种图案, 以及一个$n \times n$的黑白像素矩阵, 问矩阵对应的图案.
数据规模: $100 \le n \le 900$
大部分图像可以通过数黑白联通块的数据区分. 有两种图像不能区分, 然后用黑色像素点的比例就可以区分了.
#include <bits/stdc++.h> using namespace std; typedef pair<int, int> PII; const int MAXN = 1000 + 10; const char* exo[] = { "Baekhyun", "Chanyeol", "Chen", "D.O", "Kai", "Kris", "Lay", "Luhan", "Sehun", "Suho", "Tao", "Xiumin" }; const int bw[][2] = { {9,2}, {5, 1}, {1, 3}, {1, 2}, {2, 13}, {3, 1}, {6, 2}, {5, 8}, {5, 2}, {2, 8}, {2, 4}, {5, 2} }; int mp[MAXN][MAXN]; int N, M, cb, cw; const int dx[]={1, 0, -1, 0, 1, -1, 1, -1}; const int dy[]={0, 1, 0, -1, 1, 1, -1, -1}; void mark(int x, int y, int c) { queue<PII> Q; Q.push(PII(x, y)); mp[x][y] = -1; while (!Q.empty()) { PII nw = Q.front(); Q.pop(); for (int i = 0; i < 8; ++ i) { x = nw.first + dx[i], y = nw.second + dy[i]; if (x < 0 || y < 0 || x >= N || y >= N) continue; if (mp[x][y] != c) continue; mp[x][y] = -1; Q.push(PII(x, y)); } } } int main() { int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { scanf("%d%d", &N, &M); cb = 0, cw = 0; for (int i = 0, x = 0, y = 0; i < M; ++ i) { int c; scanf("%d", &c); if (i & 1) cb += c; else cw += c; while (c --) { mp[x][y] = i & 1; ++ y; if (y == N) ++ x, y = 0; } } int v[2] = {0, 0}; for (int i = 0; i < N; ++ i) { for (int j = 0; j < N; ++ j) { if (mp[i][j] != -1) { v[mp[i][j]] ++; mark(i, j, mp[i][j]); } } } if (v[0] == 2 && v[1] == 5) { if (cb * 10000 < 1800 * N * N) puts("Xiumin"); else puts("Sehun"); } else { for (int i = 0; i < 12; ++ i) { if (v[0] == bw[i][1] && v[1] == bw[i][0]) { puts(exo[i]); break; } } } } return 0; }
给出一个$n \times m$的格子, 还有一个方向控制panel. 在每一秒可以干如下三件事:
- 按下方向键
- 光标移动一格
- do nothing
panel每隔$P$s会旋转一下. 问从起点到终点的最短时间.
数据规模: $1 \le n,m \le 10, 1 \le P \le 50$
简单的bfs, 以$(x,y,c,t)$为状态: 当前坐标$(x,y)$, 光标和左方向键的距离$c$, 当前时间对$P$取模为$t$.
#include <bits/stdc++.h> using namespace std; typedef pair<int, int> PII; const int MAXN = 20, MAXP = 60; char mp[MAXN][MAXN]; bool mk[MAXN][MAXN][MAXP][4]; int N, M, P; struct Node { int x, y, c, p, d; Node() {} Node(int x, int y, int c, int p, int d): x(x), y(y), c(c), p(p), d(d) {} }; const int dx[] = {0, 0, -1, 1}; const int dy[] = {-1, 1, 0, 0}; void expand(int x, int y, int c, int p, int d, queue<Node> &Q) { if (x < 0 || y < 0 || x >= N || y >= M || mp[x][y] == '*') return; if (p == P) c = (c + 3) % 4, p = 0; if (mk[x][y][p][c]) return; Q.push(Node(x, y, c, p, d)); mk[x][y][p][c] = true; } int solve(PII src, PII tar) { queue<Node> Q; Q.push(Node(src.first, src.second, 0, 0, 0)); mk[src.first][src.second][0][0] = true; while (!Q.empty()) { Node nw = Q.front(); Q.pop(); if (nw.x == tar.first && nw.y == tar.second) return nw.d; // push button expand(nw.x + dx[nw.c], nw.y + dy[nw.c], nw.c, nw.p + 1, nw.d + 1, Q); // cursor move left expand(nw.x, nw.y, (nw.c + 3) % 4, nw.p + 1, nw.d + 1, Q); // cursor move right expand(nw.x, nw.y, (nw.c + 1) % 4, nw.p + 1, nw.d + 1, Q); // do nothing expand(nw.x, nw.y, nw.c, nw.p + 1, nw.d + 1, Q); } return -1; } int main() { int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { scanf("%d%d%d", &N, &M, &P); PII src, tar; for (int i = 0; i < N; ++ i) { scanf("%s", mp[i]); for (int j = 0; j < M; ++ j) { if (mp[i][j] == '@') src = PII(i, j), mp[i][j] = '.'; if (mp[i][j] == '$') tar = PII(i, j), mp[i][j] = '.'; } } memset(mk, 0, sizeof(mk)); int ret = solve(src, tar); if (ret == -1) puts("YouBadbad"); else printf("%d\n", ret); } return 0; }
给出一个半径为$r$, 高度为$h$的圆柱. 在外面包一层厚度为$d$的巧克力, 问最终的体积和表面积.
数据规模: $1 \le r, h, d \le 100$
就是积分.
体积可以考虑用Cylinder Shell(不懂得google一下)来积分
面积直接用旋转体侧面积的方法(不懂得翻微积分课本)搞就好了.
#include <bits/stdc++.h> using namespace std; const double PI = acos(-1.0); double sqr(double x) {return x * x;} int main() { int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { int r, h, d; scanf("%d%d%d", &r, &h, &d); double V = PI * (4 * d * sqr(d) + 3 * PI * r * sqr(d) + 6 * sqr(r) * d) / 3 + PI * sqr(r + d) * h; double S = 2 * PI * (sqr(r) + h * (r + d) + PI * r * d + 2 * sqr(d)); printf("%.18f %.18f\n", V, S); } return 0; }
给出两个随从的攻击力和血量, 第一个攻击第二个的结果.
应该没有人不会这题.
#include <bits/stdc++.h> using namespace std; int main() { int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { int A1, H1, A2, H2; scanf("%d%d%d%d", &A1, &H1, &A2, &H2); if (A1 == 0) puts("Invalid"); else { H1 -= A2; H2 -= A1; if (H1 <= 0) printf("Discard "); else printf("%d %d ", A1, H1); if (H2 <= 0) printf("Discard\n"); else printf("%d %d\n", A2, H2); } } return 0; }
给出$n$个数$\{a_1,a_2,\dots,a_n\}$, 问所有子集gcd的$k$次方的和.
数据规模: $1 \le n, a_i \le 10^6$
经典的容斥, 大家应该都会做.
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int MAXN = 1000000 + 10, MOD = 998244353; LL pm(LL a, LL n) { LL r = 1; for (; n; n >>= 1) { if (n & 1) r = r * a % MOD; a = a * a % MOD; } return r; } int a[MAXN], cnt[MAXN], f[MAXN]; int N, K, mx; int main() { int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { scanf("%d%d", &N, &K); mx = 0; assert(1 <= N && N <= 1000000); assert(1 <= K && K <= 1000000); for (int i = 0; i < N; ++ i) { scanf("%d", a + i); assert(1 <= a[i] && a[i] <= 1000000); cnt[a[i]] ++; mx = max(mx, a[i]); } for (int g = mx; g >= 1; -- g) { int sum = 0; f[g] = 0; for (int ng = g; ng <= mx; ng += g) { sum += cnt[ng]; f[g] = (f[g] - f[ng] + MOD) % MOD; } f[g] = (f[g] + pm(2, sum) - 1) % MOD; } int ret = 0; for (int g = 1; g <= mx; ++ g) { ret = (ret + (LL)pm(g, K) * f[g] % MOD) % MOD; } printf("%d\n", ret); for (int i = 0; i < N; ++ i) cnt[a[i]] = 0; } return 0; }
February | ||||||
---|---|---|---|---|---|---|
Sun | Mon | Tue | Wed | Thu | Fri | Sat |
26 | 27 | 28 | 29 | 30 | 31 | 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 1 |
Mon, 13 Apr 2015 21:49:04 +0800
ym...
Sat, 18 Apr 2015 01:14:34 +0800
第一题不是所有数异或一下就可以了吗?
Fri, 08 May 2015 20:11:49 +0800
第一题 3 <= N <= 100?
Mon, 11 May 2015 23:24:17 +0800
@LCLL: fixed, thx