比赛链接:传送门
跌跌撞撞6题摸银。
封榜后两题,把手上的题做完了还算舒服。就是罚时有点高。
开出了一道奇奇怪怪的题(K),然后ccpcf银应该比区域赛银要难吧,反正很开心qwq。
Problem A. Mischievous Problem Setter 00:14 (-2) Solved by Dancepted
良心签到题。WA2吃乳猪。
代码:
#include <iostream> #include <cmath> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <set> #include <vector> #include <string> #include <queue> #include <stack> #include <iomanip> #define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) #define N 100005 #define M 100005 #define INF 0x3f3f3f3f #define mk(x) (1<<x) // be conscious if mask x exceeds int #define sz(x) ((int)x.size()) #define upperdiv(a,b) (a/b + (a%b>0)) #define mp(a,b) make_pair(a, b) #define endl ‘\n‘ #define lowbit(x) (x&-x) using namespace std; typedef long long ll; typedef double db; /** fast read **/ template <typename T> inline void read(T &x) { x = 0; T fg = 1; char ch = getchar(); while (!isdigit(ch)) { if (ch == ‘-‘) fg = -1; ch = getchar(); } while (isdigit(ch)) x = x*10+ch-‘0‘, ch = getchar(); x = fg * x; } template <typename T, typename... Args> inline void read(T &x, Args &... args) { read(x), read(args...); } template <typename T> inline void write(T x) { int len = 0; char c[21]; if (x < 0) putchar(‘-‘), x = -x; do{++len; c[len] = x%10 + ‘0‘;} while (x /= 10); for (int i = len; i >= 1; i--) putchar(c[i]); } template <typename T, typename... Args> inline void write(T x, Args ... args) { write(x), write(args...); } struct Node{ int d, t; bool operator < (const Node& x) const { return d < x.d; } }nodes[N]; int main() { fast; int T; cin >> T; for (int kase = 1; kase <= T; kase++) { int n, m; cin >> n >> m; for (int i = 1; i <= n; i++) { cin >> nodes[i].d; } for (int i = 1; i <= n; i++) { cin >> nodes[i].t; } sort(nodes+1, nodes+1+n); int ans = 0; for (int i = 1; i <= n; i++) { if (nodes[i].t <= m) { m -= nodes[i].t; ans++; } else { break; } } cout << "Case " << kase << ": " << ans << endl; } return 0; }
Problem L. Ultra Weak Goldbach‘s Conjecture 00:47(+) Solved by xk (miller rabin + 素数密度 + 哥德巴赫猜想)
根据素数密度为$log^{2}N$的结论,可以用米勒-拉宾的板子O(logn)判断大素数,暴力找出比n小的最大的一个大素数。
哥德巴赫猜想在小数据范围内成立,剩下部分如果是奇数就分成2 + 2 + 3 + 两个素数,如果是偶数就是2 + 2 + 2 + 两个素数。
(xk才是真正的数学选手,我连哥德巴赫猜想都不知道,就是打酱油的)
代码:$O(T × log^{3}N)$
#include <iostream> #include <cmath> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <set> #include <vector> #include <string> #include <queue> #include <stack> #include <iomanip> #define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) #define INF 0x3f3f3f3f #define sz(x) ((int)x.size()) #define mp(a,b) make_pair(a, b) #define endl ‘\n‘ using namespace std; typedef long long ll; typedef double db; int random(int l, int r) { return rand() % (r - l) + l; } ll fmul(ll a, ll b, ll mod) { a %= mod; ll res = 0; for(;b;b>>=1) { if(b & 1) res = (res + a) % mod; a = (a + a) % mod; } return res; } ll fpow(ll a, ll b, ll mod) { ll res = 1; for(;b;b>>=1) { if(b & 1) res = fmul(res, a, mod); a = fmul(a, a, mod); } return res; } bool witness(ll a, ll n, ll u, ll t) { ll x0 = fpow(a, u, n), x1; for(int i = 1; i <= t; i++) { x1 = fmul(x0, x0, n); if(x1 == 1 && x0 != 1 && x0 != n - 1) return false; x0 = x1; } if(x1 != 1) return false; return true; } bool isprime(ll n, int times = 20) { if(n == 2) return true; if(n < 2 || !(n & 1)) return false; ll u = n - 1, t = 0; while(u % 2 == 0) { t++; u>>=1; } while(times--) { ll a = random(1, n - 1); if(!witness(a, n, u, t)) return false; } return true; } int main() { srand(time(0)); fast; int T; cin >> T; for(int kase = 1; kase <= T; kase++) { cout << "Case " << kase << ": "; ll n; cin >> n; if(n < 12) { cout << "IMPOSSIBLE\n"; continue; } for(ll i = n - 10; ; i--) { if(isprime(i)) { n -= i; cout << i; break; } } if(n & 1) { cout << " 2 2 3"; n -= 7; } else { cout << " 2 2 2"; n -= 6; } for(ll i = 2; i <= n / 2; i++) { if(isprime(i) && isprime(n - i)) { cout << ‘ ‘ << i << ‘ ‘ << n - i << endl; break; } } } }
Problem G. Pastoral Life in Stardew Valley 01:13 (+) Solved by Dancepted (平方和公式)
设$f_{n, m}$表示n × m的草地上放稻草人的方案数,则:
$f_{n, m} = \sum_{i=1}^{n-2} \sum_{j=1}^{m-2}(n-i+1) × (m-j+1) = \frac{(n-1)(n-2) × (m-1)(m-2)}{4}$
设$F_{n, m}$表示n × m的土地上的答案,则:
$F_{n, m} = \sum_{i=3}^{n}\sum_{j=3}^{m} (n-i+1)×(m-j+1)×f_{i, j} $
$= \sum_{i=3}^{n}\sum_{j=3}^{m} (n-i+1)×(m-j+1)×\frac{1}{4}i(i-1) × i(i-1)$
$= \frac{1}{4} \sum_{i=3}^{n}(n-i+1)(i-1)(i-2)\sum_{j=3}^{m}(m-j+1)(j-1)(j-2)$
令$g_{x} = \frac{1}{2} \sum_{i=3}^{x}(x-i+1)(i-1)(i-2)$,则$F_{n, m} = g_{n} * g_{m}$。
考虑预处理$g_{x}$:
①:$g_{3} = 1$
②:若已知$g_{x} = \frac{1}{2} \sum_{i=3}^{x}(x-i+1)(i-1)(i-2)$,则:
$g_{x+1} = \frac{1}{2} \sum_{i=3}^{x+1}(x-i+1+1)(i-1)(i-2)$
$= \frac{1}{2} \sum_{i=3}^{x+1}(x-i+1)(i-1)(i-2) + \frac{1}{2}\sum_{i=3}^{x+1}(i-1)(i-2) $
令$h_{x} = \frac{1}{2}\sum_{i=3}^{x}(i-1)(i-2) $,则:
$g_{x+1} = g_{x} + h_{x}$,其中,用平方和公式和等差数列求和公式可以O(1)地计算$h_{x}$。
代码:O(T + N)
#include <iostream> #include <cmath> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <set> #include <vector> #include <string> #include <queue> #include <stack> #include <iomanip> #define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) #define N 100005 #define M 100005 #define INF 0x3f3f3f3f #define mk(x) (1<<x) // be conscious if mask x exceeds int #define sz(x) ((int)x.size()) #define upperdiv(a,b) (a/b + (a%b>0)) #define mp(a,b) make_pair(a, b) #define endl ‘\n‘ #define lowbit(x) (x&-x) using namespace std; typedef long long ll; typedef double db; /** fast read **/ template <typename T> inline void read(T &x) { x = 0; T fg = 1; char ch = getchar(); while (!isdigit(ch)) { if (ch == ‘-‘) fg = -1; ch = getchar(); } while (isdigit(ch)) x = x*10+ch-‘0‘, ch = getchar(); x = fg * x; } template <typename T, typename... Args> inline void read(T &x, Args &... args) { read(x), read(args...); } template <typename T> inline void write(T x) { int len = 0; char c[21]; if (x < 0) putchar(‘-‘), x = -x; do{++len; c[len] = x%10 + ‘0‘;} while (x /= 10); for (int i = len; i >= 1; i--) putchar(c[i]); } template <typename T, typename... Args> inline void write(T x, Args ... args) { write(x), write(args...); } #define md 1000000007 ll mul(ll a, ll b) { return a * b % md; } ll add(ll a, ll b) { ll res = (a+b) % md; if (res < 0) res += md; return res; } ll fpow(ll a, ll p) { ll res = 1; for (; p; p >>= 1) { if (p & 1) res = mul(res, a); a = mul(a, a); } return res; } ll inv6, inv2; ll g[N]; ll h(ll x) { ll res = 0; res = add(res, mul(mul(x, mul(x+1, 2*x+1)), inv6)); res = add(res, mul(mul(x, x+1), inv2)); res = mul(res, inv2); return res; } void init() { g[3] = 1; for (int i = 4; i < N; i++) { g[i] = add(g[i-1], h(i-2)); } } int main() { fast; int T; cin >> T; inv2 = fpow(2, md-2); inv6 = fpow(6, md-2); init(); for (int kase = 1; kase <= T; kase++) { int n, m; cin >> n >> m; ll ans = mul(g[n], g[m]); cout << "Case " << kase << ": " << ans << endl; } return 0; }
Problem K. Mr. Panda and Kakin 02:36 (-2) Solved by Dancepted & xk (欧拉定理 逆元 素数密度)
根据欧拉定理的推论,$i^{a}$ mod n的循环节长度为$\phi(n)$,并且把n分解为$\sum_{p\in prime}p_{i}^{m_{i}}$后若$m_{i}$ <= 1,则$i^{a}$ mod n为纯循环(参考纯循环小数意会一下)。
那么只要能把$FLAG^{2^{30}+3}$凑成$FLAG^{1 mod \phi(n)}$就行了。
实际上$(x^{a})^{b} = x^{a×b}$,所以如果我们能求出$2^{30}+3$关于$phi(n)$的逆元,那么就有$(Flag^{2^{30}+3})^{逆元} = Flag^{1 mod \phi(n)} = Flag$。
而这个逆元是肯定存在的,因为$2^{30}+3$是一个质数,而且考虑到n的生成方式,n = p × q,phi(n) = (p-1)×(q-1)。而 p-1,q-1 < $2^{30}+3$,因此($2^{30}+3, \phi(n)$)= 1。
求$\phi(n)$的时候考虑素数密度,可以$O(log^{2}n)$暴力地找出n的两个素因子。
然后快速幂会爆long long,要用快速乘,然后这题的log又比较大,$log^{2}$会tle,所以要用O(1)的快速乘。
PS:第一次写脑抽了以为$(x^{a})^{c} = x^{a+b}$,幸好没过样例。
PPS:这里吹爆jls在ccpc-camp讲的数论div2,听完之后碰到欧拉定理完全不虚,然后在comet oj的直播回放里就可以看(jls的盛世美颜)了。
代码:O(T×logn)
#include <iostream> #include <cmath> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <set> #include <vector> #include <string> #include <queue> #include <stack> #include <iomanip> #define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) #define N 100005 #define M 100005 #define INF 0x3f3f3f3f #define mk(x) (1<<x) // be conscious if mask x exceeds int #define sz(x) ((int)x.size()) #define upperdiv(a,b) (a/b + (a%b>0)) #define mp(a,b) make_pair(a, b) #define endl ‘\n‘ #define lowbit(x) (x&-x) using namespace std; typedef long long ll; typedef double db; typedef long double ldb; /** fast read **/ template <typename T> inline void read(T &x) { x = 0; T fg = 1; char ch = getchar(); while (!isdigit(ch)) { if (ch == ‘-‘) fg = -1; ch = getchar(); } while (isdigit(ch)) x = x*10+ch-‘0‘, ch = getchar(); x = fg * x; } template <typename T, typename... Args> inline void read(T &x, Args &... args) { read(x), read(args...); } template <typename T> inline void write(T x) { int len = 0; char c[21]; if (x < 0) putchar(‘-‘), x = -x; do{++len; c[len] = x%10 + ‘0‘;} while (x /= 10); for (int i = len; i >= 1; i--) putchar(c[i]); } template <typename T, typename... Args> inline void write(T x, Args ... args) { write(x), write(args...); } ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a%b); } ll fmul(ll a, ll b, ll md) { a %= md, b %= md; ll c = (ldb) a * b / md; ll ans = a * b - c * md; if (ans < 0) ans += md; else if (ans >= md) ans -= md; return ans; } ll fpow(ll a, ll p, ll md) { ll res = 1; for (; p; p >>= 1) { if (p & 1) res = fmul(res, a, md); a = fmul(a, a, md); } return res; } ll exgcd(ll a, ll b, ll &x, ll &y) { if (a == 0 && b == 0) return -1; if (b == 0) {x = 1, y = 0; return a;} ll d = exgcd(b, a%b, y, x); y -= a/b*x; return d; } ll mod_reverse(ll a, ll n) { ll x, y; ll d = exgcd(a, n, x, y); if (d == 1) return (x % n + n) % n; return -1; } int main() { // fast; int T; cin >> T; for (int kase = 1; kase <= T; kase++) { ll n, c; read(n, c); ll g = gcd(n, c); ll flag = 0, phin = 0; if (g == 1) { ll x = sqrt(n+0.5); if (x % 2 == 0) x--; for (ll i = x; i >= 0; i -= 2) { if (n % i == 0) { phin = (i-1) * (n/i -1); break; } } } else { phin = (g-1) * (n/g - 1); } ll p = mod_reverse((1<<30)+3, phin); flag = fpow(c, p, n); printf("Case %d: %I64d\n", kase, flag); } return 0; } /* 3 181857896263 167005790444 218128229323 156323229335 352308724847 218566715941 */
Problem I. Cockroaches 04:19 (-1) Solved by Dancepted & lh & xk
大概是个思维题吧。。。封榜20分钟才调出来qwq。(不过好像是第一次封榜后过题?)
能消灭的最多的小强数量只有两种情况。设小强数最多的行和列对应的小强数是r和c,那么能消灭最多的数量要么是r+c,要么是r+c-1。
然后遍历小强数最多的行(列)上的小强,统计能消灭r+c和r+c-1的方案数就行了。
具体的就是遍历小强数最多的行(列)上的小强的时候,看这些小强是否恰巧在小强数最多的列(行),如果在的话,说明激光中心在这个小强所在点上时,能消灭的数量是r+c-1而不是r+c。
若r+c的数量为0,那么用同样的方法再统计一下小强数次多的行(列)与小强数最多的列(行)对r+c-1的贡献就行了。
小强的坐标上限是1e9,要离散化一下。
特别地:依次最多消灭小强数为2的时候要特判一下,防止在两个不同点消灭了两个相同小强。
代码:O(T×nlogn)
#include <iostream> #include <cmath> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <set> #include <vector> #include <string> #include <queue> #include <stack> #include <iomanip> #define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) #define N 200005 #define M 100005 #define INF 0x3f3f3f3f #define mk(x) (1<<x) // be conscious if mask x exceeds int #define sz(x) ((int)x.size()) #define upperdiv(a,b) (a/b + (a%b>0)) #define mp(a,b) make_pair(a, b) #define endl ‘\n‘ #define lowbit(x) (x&-x) using namespace std; typedef long long ll; typedef double db; /** fast read **/ template <typename T> inline void read(T &x) { x = 0; T fg = 1; char ch = getchar(); while (!isdigit(ch)) { if (ch == ‘-‘) fg = -1; ch = getchar(); } while (isdigit(ch)) x = x*10+ch-‘0‘, ch = getchar(); x = fg * x; } template <typename T, typename... Args> inline void read(T &x, Args &... args) { read(x), read(args...); } template <typename T> inline void write(T x) { int len = 0; char c[21]; if (x < 0) putchar(‘-‘), x = -x; do{++len; c[len] = x%10 + ‘0‘;} while (x /= 10); for (int i = len; i >= 1; i--) putchar(c[i]); } template <typename T, typename... Args> inline void write(T x, Args ... args) { write(x), write(args...); } int n; vector<int> vals; map<int, int> id; // int id[N<<1]; struct Node{ int r, c; }ns[N]; vector <Node> vc[N], vr[N]; int cntr1 = -1, cntr2 = -1, lenr1 = -1, lenr2 = -1; int cntc1 = -1, cntc2 = -1, lenc1 = -1, lenc2 = -1; void init() { sort(vals.begin(), vals.end()); vals.erase(unique(vals.begin(), vals.end()), vals.end()); for (int i = 0; i < sz(vals); i++) { id[vals[i]] = i; vc[i].clear(); vr[i].clear(); } for (int i = 1; i <= n; i++) { int idr = id[ns[i].r], idc = id[ns[i].c]; vr[idr].push_back(ns[i]); vc[idc].push_back(ns[i]); } cntr1 = -1, cntr2 = -1, lenr1 = -1, lenr2 = -1; cntc1 = -1, cntc2 = -1, lenc1 = -1, lenc2 = -1; for (int i = 0; i < sz(vals); i++) { if (sz(vr[i]) > lenr1) { lenr2 = lenr1; cntr2 = cntr1; lenr1 = sz(vr[i]); cntr1 = 1; } else if (sz(vr[i]) == lenr1) { cntr1++; } else if (sz(vr[i]) > lenr2) { lenr2 = sz(vr[i]); cntr2 = 1; } else if (sz(vr[i]) == lenr2) { cntr2++; } if (sz(vc[i]) > lenc1) { lenc2 = lenc1; cntc2 = cntc1; lenc1 = sz(vc[i]); cntc1 = 1; } else if (sz(vc[i]) == lenc1) { cntc1++; } else if (sz(vc[i]) > lenc2) { lenc2 = sz(vc[i]); cntc2 = 1; } else if (sz(vc[i]) == lenc2) { cntc2++; } } } int main() { fast; int T; cin >> T; for (int kase = 1; kase <= T; kase++) { cin >> n; id.clear(); vals.clear(); for (int i = 1; i <= n; i++) { read(ns[i].r, ns[i].c); vals.push_back(ns[i].r); vals.push_back(ns[i].c); } init(); ll ans1 = lenc1 + lenr1, cnt1 = 0; ll ans2 = lenc1 + lenr1 - 1, cnt2 = 0; for (int i = 0; i < sz(vals); i++) { if (sz(vc[i]) == lenc1) { cnt1 += cntr1; if (lenr2 == lenr1 - 1) { cnt2 += cntr2; } for (Node &tmp : vc[i]) { if (sz(vr[id[tmp.r]]) == lenr1) { // share same point cnt1--; cnt2++; } else if (lenr2 == lenr1 - 1 && sz(vr[id[tmp.r]]) == lenr2) { cnt2--; } } } else if (lenc2 == lenc1 - 1 && sz(vc[i]) == lenc2) { cnt2 += cntr1; for (Node &tmp: vc[i]) { if (sz(vr[id[tmp.r]]) == lenr1) { // share same point cnt2--; } } } } ll ans = 0, cnt = 0; if (cnt1 > 0) { ans = ans1, cnt = cnt1; } else { ans = ans2, cnt = cnt2; } if (ans == 2) { cnt = 1LL * n * (n-1) / 2; } printf("Case %d: %I64d %I64d\n", kase, ans, cnt); } return 0; }
Problem B. Balance of the Force 04:35(+) Solved by lh(贪心)
不能放在同一边的两个人连一条边,如果得到的图中有奇数环,则不可能。
然后枚举最小的能力值。贪心地寻找最小的最大值。
枚举下一个最小的能力值时,仅有当前最小能力值所在的环,和下一个最小能力值所在的环对应的能力值要更新,所以整个贪心可以是O(N)的。
代码:O(T×N)
#include <iostream> #include <algorithm> #include <cstdio> #include <vector> #include <cstring> #define N 200005 #define INF 0x3f3f3f3f #define fi first #define se second using namespace std; typedef pair<int,int> pii; /** fast read **/ template <typename T> inline void read(T &x) { x = 0; T fg = 1; char ch = getchar(); while (!isdigit(ch)) { if (ch == ‘-‘) fg = -1; ch = getchar(); } while (isdigit(ch)) x = x*10+ch-‘0‘, ch = getchar(); x = fg * x; } template <typename T, typename... Args> inline void read(T &x, Args &... args) { read(x), read(args...); } template <typename T> inline void write(T x) { int len = 0; char c[21]; if (x < 0) putchar(‘-‘), x = -x; do{++len; c[len] = x%10 + ‘0‘;} while (x /= 10); for (int i = len; i >= 1; i--) putchar(c[i]); } template <typename T, typename... Args> inline void write(T x, Args ... args) { write(x), write(args...); } int T; int n, m, ednum, top, col[N]; int w[N][2]; struct node { int be, w; bool operator<(const node &other)const { return w < other.w; } } s[N << 1]; struct Unite { int cur, maxn[2], minx[2]; } st[N]; int hed[N << 1], nxt[N << 1], to[N << 1]; void add(int u, int v) { to[++ednum] = v; nxt[ednum] = hed[u], hed[u] = ednum; } bool dfs(int v) { st[top].maxn[0] = max(st[top].maxn[0], w[v][col[v]]), st[top].minx[0] = min(st[top].minx[0], w[v][col[v]]); st[top].maxn[1] = max(st[top].maxn[1], w[v][col[v] ^ 1]), st[top].minx[1] = min(st[top].minx[1], w[v][col[v] ^ 1]); for (int i = hed[v]; i; i = nxt[i]) { int u = to[i]; if (col[u] == col[v]) return false; if (col[u] != -1) continue; col[u] = 1 ^ col[v]; if (dfs(u) == false) return false; } return true; } int save[N], ansmax; bool reduce() { while (top) { int id = save[top]; if (st[id].cur == 1) return false; ansmax = max(ansmax, st[id].maxn[1]), st[id].cur = 1, --top; } return true; } int main() { read(T); int u, v, cnt; int casecnt = 0; while (T--) { ++casecnt; read(n, m), ednum = top = 0, memset(hed, 0, sizeof(int) * (n + 1)), memset(col, -1, sizeof(int) * (n + 1)); for (int i = 1;i <= m; ++i) read(u, v), add(u, v), add(v, u); for (int i = 1;i <= n; ++i) read(w[i][0], w[i][1]); bool flag = true; ansmax = 0, top = 0, cnt = 0; int ans = INF; for (int i = 1;i <= n; ++i) { if (col[i] != -1) continue; ++top, st[top].cur = 0, st[top].maxn[0] = st[top].maxn[1] = 0; st[top].minx[0] = st[top].minx[1] = INF, col[i] = 0, flag &= dfs(i); if (flag == false) break; if (st[top].minx[0] > st[top].minx[1]) swap(st[top].minx[0], st[top].minx[1]), swap(st[top].maxn[0], st[top].maxn[1]); if (st[top].maxn[0] >= st[top].maxn[1]) st[top].cur = 1; else s[++cnt] = node{top, st[top].minx[0]}; s[++cnt] = node{top, st[top].minx[1]}; ansmax = max(ansmax, st[top].maxn[st[top].cur]); } printf("Case %d: ", casecnt); if (flag == false) { puts("IMPOSSIBLE"); continue; } sort(s + 1, s + 1 + cnt); int i = 1; top = 0; while (flag && i <= cnt) { ans = min(ans, ansmax - s[i].w); save[++top] = s[i].be; if (s[i].w != s[i + 1].w) { flag &= reduce(); if (!flag) break; } ++i; } write(ans), putchar(‘\n‘); } return 0; }
还有不到一周就是CCPC-Final了,这周每两天一套题,冲鸭。
总结:
浮躁的菜逼选手贡献全部罚时。
原文地址:https://www.cnblogs.com/Lubixiaosi-Zhaocao/p/11831096.html