CSP2020题解
T1:儒略日
Description
略
Solution
考虑按特殊日期分段,然后每段的4年/400年是一样的于是就可以把循环的范围缩小,然后就是各种讨论
写的时候和考后发现的问题是:
- 第一年是366天先减了个365
- 1582年10月会输出32日
- 1582年前的闰年会输出3月0日
- 没开long long
主要都是细节的问题
#include<bits/stdc++.h> using namespace std; #define LL long long inline LL read() { LL f = 1,x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0'||ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0'&&ch <= '9'); return f*x; } const int Julian = 4713; int Q; int num[14] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; int ansY,ansM,ansD; LL tot; inline bool is_rn1(int x) { if((x - 1) % 4 == 0) return 1; else return 0; } inline bool is_rn2(int x) { if(x % 4 == 0) return 1; return 0; } inline bool is_rn3(int x) { if(x % 400 == 0) return 1; else if(x % 100 == 0) return 0; else if(x % 4 == 0) return 1; return 0; } int main() { LL a = ((4713 - 1) / 4) * (365 * 3 + 366) + 366; LL b = (365 * 3 + 366); LL c = ((1582 - 1) / 4) * b; c += 365; LL d = c + 355; tot = 400 * 365 + 97; Q = read(); while(Q--) { LL r = read(); ansY = 0,ansM = 1,ansD = 1; if(r < a) { int res1 = r / (b);// 4 years ansY = 4713 - res1 * 4; r -= res1 * b; if(r >= 366) { r-=366,ansY--;while(r >= 365) r-=365,ansY--;} int cur = num[1]; for(int i=1;i<=11&&r>=cur;i++) { r -= cur; cur = num[i+1]; if(is_rn1(ansY)&&i == 1) cur++; ansM++; } ansD += r; printf("%d %d %d BC ",ansD,ansM,ansY); } else if(r >= a) { r -= a; ansY = 1,ansM = 1,ansD = 1; LL res1 = r/b; if(r < c)// 1 ~ 1582 { r -= res1 * b; ansY = 1 + res1 * 4; if(r >= 365) r-= 365,ansY++; if(r >= 365) r-= 365,ansY++; if(r >= 365) r -= 365,ansY++; if(r >= 366) r-=366,ansY++; int cur = num[1]; for(int i=1;i<=11&&r>=cur;i++) { r -= cur; cur = num[i+1]; if(is_rn2(ansY)&&i == 1) cur++; ansM++; } ansD += r; printf("%d %d %d ",ansD,ansM,ansY); } else if(r >= c && r < d) { ansY = 1582;ansM = 1;ansD = 1; r -= c; for(int i=1;i<=12&&(r>=num[i]||(i == 10 && (r + 10 >= num[i])));i++) { int cur = num[i]; if(i == 10 && r + 10 >= num[i]) r = r + 10 - num[i]; else r -= num[i]; ansM++; } if(ansM == 10) { if(r < 4) ansD += r; else ansD += r + 10; } else ansD += r; printf("%d %d %d ",ansD,ansM,ansY); } else if(r >= d) { ansY = 1583; r -= d; int res1 = r/tot; ansY += res1 * 400; r -= res1 * tot; while(r) { /*if(r >= 365 * 3 + 366) { r -= 365 * 3 + 366; ansY += 4; continue; } */ bool fl = is_rn3(ansY); // cout << fl << endl; if(fl&&r>=366) { r -= 366; ansY++; continue; } if(fl==0&&r>=365) { r-= 365; ansY++; continue; } //if(flag) cout << ansY << " " << flag << endl; ansM = 1,ansD = 1; int cur = num[1]; for(int i=1;i<=11&&r>=cur;i++) { r -= cur; ansM++; cur = num[i+1]; if(fl&&i == 1) cur++; } if(r < cur) ansD += r,r = 0; if(!r) break; ansY++; } printf("%d %d %d ",ansD,ansM,ansY); } } } return 0; }
T2 动物园
Description
略
Solution
略
需要注意的是特殊边界
#include<bits/stdc++.h> using namespace std; #define LL long long #define ULL unsigned long long inline LL read() { LL f = 1,x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0'||ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0'&&ch <= '9'); return f*x; } const int MAXN = 1e6 + 10; int n,m,c,k; ULL a[MAXN]; int p[MAXN]; int col[MAXN]; vector<int>G[100 + 10]; int main() { n = read(),m = read(),c = read(),k = read(); ULL sum = 0; if(n == 0 && k == 64) { cout << "18446744073709551616" << endl; return 0; } for(int i=1;i<=n;i++) a[i] = read(),sum |= a[i]; for(int i=1;i<=m;i++) { p[i] = read();int q = read(); G[p[i]].push_back(i); } for(int i=0;i<k;i++) { if((1ULL << i) & sum) { for(int j=0;j<G[i].size();j++) { col[G[i][j]] = 1; } } } int cur = 0; for(int i=0;i<k;i++) { int res = 1; for(int j=0;j<G[i].size();j++) res &= col[G[i][j]]; if(res) cur++; } cout << (1ULL << cur) - n << endl; return 0; }
T3 函数调用
Description
略
Solution
考虑到乘法是全局操作,很容易想到把答案转化为原数值乘上所以乘法操作 加上 每个加法操作最后的值
显然加法操作最后的值只会被后面涉及到他的乘法操作影响
于是考虑维护一个操作的后缀积
这个后缀积由两部分组成,一部分是后面独立的操作,一部分有可能是当前这个操作体系中在他后面的乘法操作
这道题的核心就在于如何维护一个操作体系中乘法操作的影响
显然这样的一个操作体系是个DAG,可以拓扑做
考场上思路比较乱,写的有点麻烦,差点没调出来。。。
#include<bits/stdc++.h> using namespace std; #define LL long long inline LL read() { LL f = 1,x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0'||ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0'&&ch <= '9'); return f*x; } const int MAXM = 1000000 + 5; const int MAXN = 200000 + 10; const int MOD = 998244353; int n,m; LL a[MAXN]; struct Query { int opt; LL v; LL vv; }b[MAXN]; LL mull[MAXN],val[MAXN]; struct Edge { int to; int next; LL w; }G[MAXM + MAXN]; int head[MAXN],cnt; int Q; int f[MAXN]; int deg[MAXN]; LL W[MAXN]; bool vis[MAXN]; queue<int>q; inline void addedge(int u,int v) { G[++cnt].to = v;G[cnt].next = head[u];head[u] = cnt;return; } inline void dfs(int x) { vis[x] = 1; int sz = 0; LL res = 1; for(int i=head[x];i;i=G[i].next) { sz++; int v = G[i].to; if(v > m) { return; } if(!vis[v]) dfs(v); G[i].w = res; res = res * val[v] % MOD; } if(sz) val[x] = res; return; } inline void solve() { for(int i=1;i<=m;i++) { if(!deg[i]) dfs(i); } } LL dp[MAXN],add[MAXN]; inline void toppsort() { for(int i=1;i<=m;i++) dp[i] = W[i]; for(int i=1;i<=m;i++) if(!deg[i]) q.push(i),dp[i] = W[i]; while(q.size()) { int u = q.front();q.pop(); for(int i=head[u];i;i=G[i].next) { int v = G[i].to; if(v > m){add[v - m] = (add[v - m] + dp[u] * b[u].vv%MOD) % MOD;} else dp[v] = (dp[v] + dp[u] * G[i].w % MOD) % MOD; deg[v]--; if(!deg[v]) q.push(v); } } } int main() { n = read(); for(int i=1;i<=n;i++) a[i] = read(); m = read(); for(int i=1;i<=m;i++) { val[i] = 1; b[i].opt = read(),b[i].v = read(); if(b[i].opt == 1) b[i].vv = read(),addedge(i,b[i].v + m),deg[b[i].v+m]++; if(b[i].opt == 2) val[i] = val[i] * b[i].v % MOD; if(b[i].opt == 3) { for(int j=1;j<=b[i].v;j++) { int cur = read(); addedge(i,cur); deg[cur]++; } } } solve(); Q = read(); mull[Q+1] = 1; for(int i=1;i<=Q;i++) mull[i] = 1,f[i] = read(); for(int i=Q;i>=1;i--) mull[i] = mull[i+1] * val[f[i]] % MOD; for(int i=1;i<=Q;i++) { if(b[f[i]].opt == 3) W[f[i]] += mull[i+1],W[f[i]] %= MOD; } toppsort(); LL Mul = 1; for(int i=1;i<=Q;i++) { Mul = Mul * val[f[i]] % MOD; if(b[f[i]].opt == 1) add[b[f[i]].v] = (add[b[f[i]].v] + b[f[i]].vv * mull[i+1] % MOD) % MOD; } for(int i=1;i<=n;i++) printf("%lld ",(a[i] * Mul % MOD + add[i]) % MOD); return 0; } /* 3 1 2 3 2 2 2 3 2 1 1 2 1 2 */
T4 贪吃蛇
Description
略
Solution
考试时大概想到了70pts的做法,但是由于T3写太久了,没时间了。。。
容易想到两点:
- 如果这只蛇吃了只会不是最弱的蛇,那么他一定会吃
- 如果这只蛇吃了变成最弱的蛇且第二强的蛇吃掉他后不会变成最弱的蛇,他就一定不会吃
第二点的正确性取决于第一点 ,考虑第一点的不严谨证明:
当前最强,次强,最弱,次弱 : A,B,C,D,显然 A-D > B-C
A吃了D之后原序列变成了 B ,A-D,C,这时B吃了C后在原序列A-D的后面,也就是说A-D永远不可能成为最弱的
有了第一个结论,第二个结论显然
于是吃了变成最弱的蛇就变成了一个递归的问题,显然和到问题递归的层数相关
这个最大值,最小值显然可以拿set维护
但实际上每次吃掉后的蛇依旧有单调性,所以想“蚯蚓”那样维护即可
#include<bits/stdc++.h> using namespace std; inline int read() { int f = 1,x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0'||ch > '9'); do { x = (x << 3) + (x << 1) + ch - '0'; ch = getchar(); }while(ch >= '0' && ch <= '9'); return f*x; } const int MAXN = 1000000 + 10; int T; int n; int a[MAXN]; pair<int,int>q1[MAXN],q2[MAXN]; int l1,l2,r1,r2,ans; int Case; inline void clear() { memset(q1,0,sizeof(q1)); memset(q2,0,sizeof(q2)); l1 = 1;r1 = 0; l2 = 1;r2 = 0; } inline int size1() { return r1 - l1 + 1; } inline int size2() { return r2 - l2 + 1; } inline pair<int,int> checkmax() { pair<int,int> res; if(r2 < l2 ||(r1 >= l1 && q1[r1] > q2[l2])) { res = q1[r1];r1--; } else res = q2[l2],l2++; return res; } int main() { // freopen("snakes4.in","r",stdin); T = read();n = read(); while(T--) { ++Case; ans = 0; clear(); if(Case == 1) for(int i=1;i<=n;i++) a[i] = read(); else { int k = read(); for(int i=1;i<=k;i++) { int x = read(),y = read(); a[x] = y; } } for(int i=1;i<=n;i++) { pair<int,int> cur = make_pair(a[i],i); q1[++r1] = cur; } while(1) { if(size1() + size2() == 2) { ans = 1; break; } pair<int,int> cur_min = q1[l1];l1++; pair<int,int> cur_max = checkmax(); if(r1 >= l1 && q1[l1] < make_pair(cur_max.first - cur_min.first,cur_max.second)) q2[++r2] = make_pair(cur_max.first - cur_min.first,cur_max.second); else { pair<int,int>cur = make_pair(cur_max.first - cur_min.first,cur_max.second); ans = size1() + size2() + 2; int cnt = 0,res = 0; while(1) { cnt++; if(size1() + size2() == 1) { if(cnt % 2 == 0) res++; break; } pair<int,int> now = checkmax();now.first -= cur.first; if((r1 >= l1 || r2 >= l2) && now > min(r2 >= l2 ?q2[r2]:make_pair(1<<30,1<<30),r1>=l1?q1[l1]:make_pair(1<<30,1<<30))) { if(cnt % 2 == 0) res++; break; } cur = make_pair(now.first ,now.second); } ans -= res; break; } } printf("%d ",ans); } }