The 12th Zhejiang Provincial Collegiate Programming Contest - Onsite | ||
---|---|---|
A | Ace of Aces | 47.17% (267/566) |
B | Team Formation | 17.88% (83/464) |
C | Convex Hull | 11.36% (5/44) |
D | Beauty of Array | 28.75% (90/313) |
E | Floor Function | 0.56% (1/178) |
F | Permutation Graph | 0.00% (0/27) |
G | Lunch Time | 59.10% (237/401) |
H | May Day Holiday | 40.06% (264/659) |
I | Earthstone Keeper | 3.47% (5/144) |
J | Convert QWERTY to Dvorak | 23.33% (210/900) |
K | Capture the Flag | 12.12% (45/371) |
L | Demacia of the Ancients | 71.86% (304/423) |
上面的是现场过题的情况.
The 12th Zhejiang Provincial Collegiate Programming Contest - Online | ||
---|---|---|
A | Ace of Aces | 49.03% (383/781) |
B | Team Formation | 21.22% (97/457) |
C | Convex Hull | 15.78% (6/38) |
D | Beauty of Array | 26.80% (100/373) |
E | Floor Function | 4.47% (3/67) |
F | Permutation Graph | 5.88% (2/34) |
G | Lunch Time | 66.73% (309/463) |
H | May Day Holiday | 41.63% (291/699) |
I | Earthstone Keeper | 3.22% (2/62) |
J | Convert QWERTY to Dvorak | 25.29% (238/941) |
K | Capture the Flag | 27.45% (28/102) |
L | Demacia of the Ancients | 89.00% (413/464) |
这个是同步赛过题的情况. 水题偏多, 基本上每题都有人过, 虽然现场F题没有人过. E题原本是作为压轴的智商题的, 过的人还是有不少的.
给出$n$个数, 找出出现次数最多的那个数.
数据规模: $1 \le n, a_i \le 1000$
应该没有人不会做.
#include <bits/stdc++.h> using namespace std; int main() { int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { vector<int> cnt(1001, 0); int n; scanf("%d", &n); for (int i = 0; i < n; ++ i) { int x; scanf("%d", &x); cnt[x] ++; } int ret = max_element(cnt.begin(), cnt.end()) - cnt.begin(); for (int i = 0; i <= 1000; ++ i) { if (cnt[i] == cnt[ret] && ret != i) { ret = -1; break; } } if (ret == -1) puts("Nobody"); else printf("%d\n", ret); } return 0; }
给出$n$个数$a_1,a_2,\dots,a_n$, 问有多少对$(i,j)$满足$a_i \oplus a_j > \max\{a_i,a_j\}$.
数据规模: $1 \le n \le 10^5, 1 \le a_i \le 10^9$
枚举第几位开始变大即可. 用trie树或者数组维护都可.
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int SZ = 4000000 + 10; struct Node { Node *ch[2]; int sz; } pool[SZ], *rt, *null, *pt; Node *newNode() { pt->ch[0] = pt->ch[1] = null; pt->sz = 0; return pt ++; } void ins(int x) { Node *p = rt; p->sz ++; for (int i = 31; i >= 0; -- i) { int o = (x >> i) & 1; if (p->ch[o] == null) p->ch[o] = newNode(); p = p->ch[o]; p->sz ++; } } int cnt(int x) { Node *p = rt; int ret = 0; for (int i = 31; i >= 0; -- i) { int o = (x >> i) & 1; if (o == 0) ret += p->ch[1]->sz; p = p->ch[0]; } return ret; } int A[SZ], N; int main() { int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { pt = pool; null = newNode(); null->ch[0] = null->ch[1] = null; rt = newNode(); scanf("%d", &N); LL ret = 0; for (int i = 0; i < N; ++ i) scanf("%d", A + i); sort(A, A + N); for (int i = 0; i < N; ++ i) { ret += cnt(A[i]); ins(A[i]); } printf("%lld\n", ret); } return 0; }
给出$n$个点, 任意三点不共线. 求出所有可能凸包的面积和的两倍.
数据规模: $1 \le n \le 1000, |x_i|, |y_i| \le 10^9$
做过GCJ2015 Round 1A的应该都会做. 没做过的应该想想就可以搞出来.
回忆凸包面积的公式, 只需要考虑每条边对答案的贡献即可.
然后枚举一个端点, 另一个端点极角排序, 就可以很快统计出答案.
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int MAXN = 2000, MOD = 998244353; inline int sgn(LL x) {return x < 0 ? -1 : x > 0;} inline LL sqr(LL x) {return x * x;} struct Point { int x, y; Point() {} Point(int x, int y): x(x), y(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 len2() const { return (LL)x * x + (LL)y * y; } }; struct AngleCmp { bool operator() (const Point &A, const Point &B) { int sa(sgn(A.y)), sb(sgn(B.y)); if (A.y == 0 && A.x < 0) return false; if (B.y == 0 && B.x < 0) return true; return sa != sb ? sa < sb : A.det(B) > 0; } }; Point P[MAXN], Q[MAXN]; LL pw[MAXN]; int N, M; int main() { pw[0] = 1; for (int i = 1; i < MAXN; ++ i) pw[i] = pw[i - 1] * 2 % MOD; int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { scanf("%d", &N); for (int i = 0; i < N; ++ i) { scanf("%d%d", &P[i].x, &P[i].y); } int ret = 0; for (int i = 0; i < N; ++ i) { for (int j = (M = 0); j < N; ++ j) if (i != j) { Q[M ++] = P[j] - P[i]; } sort(Q, Q + M, AngleCmp()); for (int j = 0, k = 0, cnt = 0; j < M; ++ j) { if (j == k) k = (k + 1) % M; for (; k != j && Q[j].det(Q[k]) > 0; k = (k + 1) % M) ++ cnt; ret = (ret + P[i].det(P[i] + Q[j]) % MOD * (pw[cnt] - 1)) % MOD; ret = (ret + MOD) % MOD; if (cnt) -- cnt; } } printf("%d\n", ret); } return 0; }
给出$n$个数$a_1,a_2,\dots,a_n$, 对于所有连续子序列, 求出本质不同数的和.
数据规模: $1 \le n \le 10^5, 1 \le a_i \le 10^6$
考虑每个数字对答案的贡献.
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef vector<int> VI; const int MAXN = 100000 + 10; map<int, VI> mp; int main() { int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { int n; scanf("%d", &n); mp.clear(); for (int i = 1; i <= n; ++ i) { int v; scanf("%d", &v); mp[v].push_back(i); } LL ret = 0; for (auto &x : mp) { VI &v = x.second; int pv = 0; for (size_t i = 0; i < v.size(); ++ i) { ret += (LL)x.first * (n - v[i] + 1) * (v[i] - pv); pv = v[i]; } } printf("%lld\n", ret); } return 0; }
给定$a,b,c,d$, 满足$bc > ad$, 求使得$cn-d \lfloor \frac{a}{b}n\rfloor$最小的最小的$n$.
数据规模: $1 \le a, b, c, d \le 10^{18}$
令$y = \lfloor \frac{a}{b}x\rfloor, x = \lceil \frac{by}{a} \rceil$, 原问题等价于求$cx-dy$的最小值, $ax \ge by, x \ge 1, y \ge 0$.
分两种情况考虑
1. $a \ge b$
令$k = \lfloor \frac{a}{b}\rfloor, y \ge kx, c > ad / b \ge kd$
令$a^\prime=a-kb, c^\prime=c-kd,y^\prime=y-kx$, 此时原问题的约数条件任然满足, 问题可以缩小规模.
原问题的解$(x,y)$对应现在的解$(x,kx+y^\prime)$
显然若$a^\prime=0$, 我们可以得到$(x,y^\prime)=(1,0)$
2. $a < b$
令$k = \lfloor \frac{b-1}{a}\rfloor, x > ky$
令$b^\prime=b-ka, d^\prime=d-k*c, x^\prime=x-ky$
类似的, 原问题的约数条件任然满足, 问题可以缩小规模.
显然若$d^\prime \le 0$, 我们可以得到$(x^\prime,y)=(1,0)$
#include <bits/stdc++.h> using namespace std; typedef long long LL; void solve(LL a, LL b, LL c, LL d, LL &x, LL &y) { if (a == 0 || d <= 0) {x = 1, y = 0; return;} if (a >= b) { LL k = a / b; solve(a - k * b, b, c - k * d, d, x, y); y += k * x; } else { LL k = (b - 1) / a; if (d / c < k) x = 1, y = 0; else solve(a, b - k * a, c, d - k * c, x, y); x += k * y; } } int main() { int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { LL a, b, c, d; scanf("%lld%lld%lld%lld", &a, &b, &c, &d); LL x, y; solve(a, b, c, d, x, y); printf("%lld\n", x); } return 0; }
给一个算法:
输入:$1 \sim n$的排列$a_1, a_2,\dots, a_n$
中间处理:对每个逆序对$(a_i, a_j)$连一条无向边,$i < j, a_i > a_j$ ($a_i$和$a_j$连一条边).
输出:无向图的联通分量。
现在给出这个算法的输出,问有多少个输入对应这个输出。 即给出某个无向图的联通分量,问有多少排列满足上述算法。
数据规模: $1 \le n \le 10^5$
有个显然的结论, 每个联通分量里面数字的标号是连续的. 于是可以先判断下给定的联通块是否合法, 如果不合法输出0. 假设某个联通块的点标号在$l$和$r$之间, 那么这些点在排列中的位置貌似也只能在$l$和$r$之间(即只能填在$p_l,p_{l+1},\dots,p_r$上)
显然答案只和$r-l+1$的大小有关, 不妨令$n=r-l+1$, $f(n)$表示长度为$n$的答案.
很容易得到公式: $f(n) = n! - \displaystyle\sum_{k=1}^{n-1}(n-k)!f(k)$
解释一下: 枚举1所在联通分量的大小为$k$, 根据标号连续这一点, 我们可以知道肯定是排列中前$k$个数, 后面$n-k$个数随便排列, 于是方案数为$f(k) \cdot (n - k)!$
直接递推是$n^2$的, 观察可以发现公式右边部分有一个卷积, 我们可以使用ntt优化(由于$786433=3 \times 2^{18}+1$, 原根是10).
有两种优化方法, 分块优化和cdq分治优化. 会的人应该就明白了, 不会的慢慢研究标程.
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int MAXN = 400000 + 10, SZ = 100000 + 10; const int P = 786433, g = 10; LL f[MAXN], h[MAXN]; LL pm(LL a, LL n, LL m) { LL r=1; for (;n;n>>=1,a=a*a%m) if (n&1) r=r*a%m; return r; } void NTT(LL a[], int n, bool inv=false) { LL w=1,d=pm(g,(P-1)/n,P),t; int i,j,c,s; if (inv) { for (i=1,j=n-1;i<j;swap(a[i++],a[j--])); for (t=pm(n,P-2,P),i=0;i<n;++i) a[i]=a[i]*t%P; } for (s=n>>1;s;s>>=w=1,d=d*d%P) { for (c=0;c<s;++c,w=w*d%P) { for (i=c;i<n;i+=s<<1) { a[i|s]=(a[i]+P-(t=a[i|s]))*w%P; a[i]=(a[i]+t)%P; } } } for (i=1;i<n;++i) { for (j=0,s=i,c=n>>1;c;c>>=1,s>>=1) j=j<<1|s&1; if (i<j) swap(a[i],a[j]); } } void solve(int l, int r) { if (r - l == 1) { h[l] = (f[l] - h[l]) % P; h[l] = (h[l] + P) % P; return; } int m = (l + r) >> 1; solve(l, m); static LL A[MAXN], B[MAXN]; int s = 1, n = m - l + 1; while (s < n * 2) s <<= 1; for (int i = 1; i < s; ++ i) A[i] = i < n ? h[i + l - 1] : 0; for (int i = 1; i < s; ++ i) B[i] = f[i]; A[0] = B[0] = 0; NTT(A, s); NTT(B, s); for (int i = 0; i < s; ++ i) A[i] = A[i] * B[i] % P; NTT(A, s, true); for (int i = m; i < r; ++ i) h[i] += A[i - l + 1]; solve(m, r); } int main() { f[0] = 1; for (int i = 1; i < SZ; ++ i) f[i] = f[i - 1] * i % P; solve(1, SZ); int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { int n, m; scanf("%d%d", &n, &m); LL ret = 1; for (int i = 0; i < m; ++ i) { int c; scanf("%d", &c); int l = n, r = 0; ret = ret * h[c] % P; for (int j = 0; j < c; ++ j) { int x; scanf("%d", &x); l = min(l, x); r = max(r, x); } if (r - l + 1 != c) ret = 0; } printf("%lld\n", ret); } return 0; }
应该没有人不会这题..
#include <bits/stdc++.h> using namespace std; typedef pair<int, string> PIS; int main() { int T; cin >> T; for (int _ = 0; _ < T; ++ _) { int s, m, d; cin >> s >> m >> d; vector<PIS> S(s), M(m), D(d); for (int i = 0; i < s; ++ i) cin >> S[i].second >> S[i].first; for (int i = 0; i < m; ++ i) cin >> M[i].second >> M[i].first; for (int i = 0; i < d; ++ i) cin >> D[i].second >> D[i].first; sort(S.begin(), S.end()); sort(M.begin(), M.end()); sort(D.begin(), D.end()); cout << S[s / 2].first + M[m / 2].first + D[d / 2].first << " "; cout << S[s / 2].second << " " << M[m / 2].second << " " << D[d / 2].second << endl; } return 0; }
计算某年的5月1日是星期几.
应该没有人不会这题
#include <bits/stdc++.h> using namespace std; int toWeek(int y, int m, int d) { int tm = m >= 3 ? m - 2 : m + 10; int ty = m >= 3 ? y : y - 1; return (ty + ty / 4 - ty / 100 + ty / 400 + (int)(2.6 * tm - 0.2) + d) % 7; } int main() { int T; scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { int y; scanf("%d", &y); const int a[] = {6, 9, 6, 5, 5, 5, 5}; printf("%d\n", a[toWeek(y, 5, 1)]); } return 0; }
有一个$n \times m$的格子, 有障碍物, 有陷阱, 有怪物, 保证怪物不相邻. 人只要和怪物相邻就需要杀死怪物, 怪物死了之后就消失. 人走到陷阱上会触发陷阱, 陷阱不会消失. 杀死怪物以及触发陷阱都会掉一定量的血.
给出起点和终点, 在掉血量最小的前提下花最少的时间.
数据规模: $1 \le n, m \le 500$
由于怪物不相邻, 直接可以搞一个双关键字最短路. 注意伤害不要重复计算.
图建的不好的话spfa可能会超时, 建议使用dijkstra.
#include <bits/stdc++.h> using namespace std; const int MAXN = 500 + 10; const int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1}; typedef pair<int, int> PII; typedef pair<PII, PII> Node; struct Edge { int x, y, f, t, nx; Edge() {} Edge(int x, int y, int f, int t, int nx): x(x), y(y), f(f), t(t), nx(nx) {} } E[MAXN * MAXN << 4]; char mp[MAXN][MAXN]; int G[MAXN][MAXN], sz; int N, M, SR, SC, TR, TC; PII operator + (const PII &a, const PII &b) { return PII(a.first + b.first, a.second + b.second); } bool fit(int x, int y) { return x >= 1 && x <= N && y >= 1 && y <= M && mp[x][y] != '#'; } bool block(int x, int y) { return !fit(x, y) || isupper(mp[x][y]); } int trap(int x, int y) { return islower(mp[x][y]) ? mp[x][y] - 'a' + 1 : 0; } int monster(int x, int y) { return isupper(mp[x][y]) ? mp[x][y] - 'A' + 1 : 0; } int main() { int T; scanf("%d", &T); while (T --) { scanf("%d%d%d%d%d%d", &N, &M, &SR, &SC, &TR, &TC); for (int i = 1; i <= N; ++ i) scanf("%s", mp[i] + 1); memset(G, -1, sizeof(G)); sz = 0; for (int x = 1; x <= N; ++ x) { for (int y = 1; y <= M; ++ y) if (!block(x, y)) { for (int k = 0; k < 4; ++ k) { int x1 = x + dx[k], y1 = y + dy[k], ft = 0; if (block(x1, y1)) continue; for (int i = 0; i < 4; ++ i) { int x2 = x1 + dx[i], y2 = y1 + dy[i]; if (fit(x2, y2)) ft += monster(x2, y2); } E[sz] = Edge(x1, y1, ft, 1, G[x][y]); G[x][y] = sz ++; } } } for (int x = 1; x <= N; ++ x) { for (int y = 1; y <= M; ++ y) if (monster(x, y)) { for (int i = 0; i < 4; ++ i) { int x1 = x + dx[i], y1 = y + dy[i]; if (!fit(x1, y1)) continue; for (int j = 0; j < 4; ++ j) if (i != j) { int x2 = x + dx[j], y2 = y + dy[j], ft = 0; if (!fit(x2, y2)) continue; for (int k = 0; k < 4; ++ k) { int xx = x2 + dx[k], yy = y2 + dy[k]; if (!fit(xx, yy) || abs(xx - x1) + abs(yy - y1) <= 1) continue; ft += monster(xx, yy); } E[sz] = Edge(x2, y2, ft, 2, G[x1][y1]); G[x1][y1] = sz ++; } } } } static PII dis[MAXN][MAXN]; memset(dis, 0x7f, sizeof(dis)); priority_queue<Node, vector<Node>, greater<Node> > Q; dis[SR][SC] = PII(0, 0); Q.push(Node(dis[SR][SC], PII(SR, SC))); while (!Q.empty()) { int x = Q.top().second.first, y = Q.top().second.second; Q.pop(); if (x == TR && y == TC) break; for (int it = G[x][y]; ~it; it = E[it].nx) { int xx = E[it].x, yy = E[it].y; PII cost = PII(E[it].f + trap(xx, yy), E[it].t); if (dis[xx][yy] > dis[x][y] + cost) { dis[xx][yy] = dis[x][y] + cost; Q.push(Node(dis[xx][yy], PII(xx, yy))); } } } printf("%d %d\n", dis[TR][TC].first, dis[TR][TC].second); } return 0; }
给出两种键盘布局, 转化一下.
应该没有人不会这题.
#include <bits/stdc++.h> using namespace std; char *qw = "~!@#$%^&*()_+`1234567890-=\tQWERTYUIOP{}|qwertyuiop[]\\ASDFGHJKL:\"\nasdfghjkl;'ZXCVBNM<>?zxcvbnm,./ "; char *dv = "~!@#$%^&*(){}`1234567890[]\t\"<>PYFGCRL?+|',.pyfgcrl/=\\AOEUIDHTNS_\naoeuidhtns-:QJKXBMWVZ;qjkxbmwvz "; int main() { map<char, char> mp; for (char *p = qw, *q = dv; *p; ++ p, ++ q) mp[*p] = *q; for (char c; c = getchar(), c != EOF;) putchar(mp[c]); return 0; }
规则复杂, 不好描述.
看懂题目直接模拟.
#include <bits/stdc++.h> using namespace std; typedef double flt; const int MAXN = 100 + 10, MAXQ = 20; const flt eps = 1e-8; bool ck[MAXQ][MAXN][MAXN]; int mt[MAXQ][MAXN], rk[MAXN]; flt score[MAXN]; int N, Q, S, C; int main() { int T; cin >> T; //scanf("%d", &T); for (int _ = 0; _ < T; ++ _) { cin >> N >> Q >> S >> C; //scanf("%d%d%d%d", &N, &Q, &S, &C); for (int i = 1; i <= N; ++ i) score[i] = S; for (int c = 0; c < C; ++ c) { memset(ck, 0, sizeof(ck)); int A; cin >> A; //scanf("%d", &A); for (int i = 0; i < A; ++ i) { int ak, df, sr; cin >> ak >> df >> sr; //scanf("%d%d%d", &ak, &df, &sr); ck[sr][df][ak] = true; } for (int i = 1; i <= Q; ++ i) { for (int j = 1; j <= N; ++ j) { cin >> mt[i][j]; //scanf("%d", &mt[i][j]); } } for (int sr = 1; sr <= Q; ++ sr) { int mt = 0; for (int i = 1; i <= N; ++ i) { int ak = 0; mt += ::mt[sr][i]; for (int j = 1; j <= N; ++ j) ak += ck[sr][i][j]; if (!ak) continue; score[i] -= N - 1; for (int j = 1; j <= N; ++ j) score[j] += ck[sr][i][j] * flt(N - 1) / ak; } for (int i = 1; i <= N; ++ i) { if (!::mt[sr][i]) score[i] -= N - 1; else score[i] += flt(N - 1) * (N - mt) / mt; } } for (int i = 1; i <= N; ++ i) { rk[i] = 1; for (int j = 1; j <= N; ++ j) if (i != j) { if (score[j] > score[i] + eps) ++ rk[i]; } } int U; cin >> U; //scanf("%d", &U); for (int i = 0; i < U; ++ i) { int x; cin >> x; //scanf("%d", &x); printf("%.10f %d\n", score[x], rk[x]); } } } return 0; }
应该没有人不会这题.
#include <bits/stdc++.h> using namespace std; int main() { int T; cin >> T; for (int _ = 0; _ < T; ++ _) { int n; scanf("%d", &n); for (int i = n; i >= 1; -- i) { int x; scanf("%d", &x); n -= x <= 6000; } printf("%d\n", n); } 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 |
Tue, 28 Apr 2015 08:43:40 +0800
膜拜大神。这次你有命题吗?
Tue, 28 Apr 2015 22:34:29 +0800
@yygy: BCDF是我出的
Wed, 27 May 2015 15:35:46 +0800
请问神牛qq,有一道你出的题目想请教您一下
Thu, 28 May 2015 12:01:45 +0800
@Stubird: 894489107
Wed, 17 Jun 2015 00:17:31 +0800
nice!