AtCoder Beginner Contest 446
个人题解
A
#include<bits/stdc++.h>using namespace std;typedef long long ll;typedef unsigned long long ull;typedef pair<int,int> PII;#define MP make_pair#define fi first#define se second#define pb push_back#define eb emplace_back#define SET(a,b) memset(a,b,sizeof(a))#define CPY(a,b) memcpy(a,b,sizeof(b))#define rep(i,j,k) for(int i=(j);i<=(k);++i)#define per(i,j,k) for(int i=(j);i>=(k);--i)int read() { int a=0, f=1; char c=getchar(); while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); } while(isdigit(c)) a=a*10+c-'0', c=getchar(); return a*f;}signed main() { // ios::sync_with_stdio(0); // cin.tie(0); // cout.tie(0); string s; cin >> s; cout << "Of"; for(auto x : s) { if(x < 'a' || x > 'z') cout << (char)(x - 'A' + 'a'); else cout << x; } cout << endl; return 0;}B
#include<bits/stdc++.h>using namespace std;typedef long long ll;typedef unsigned long long ull;typedef pair<int,int> PII;#define MP make_pair#define fi first#define se second#define pb push_back#define eb emplace_back#define SET(a,b) memset(a,b,sizeof(a))#define CPY(a,b) memcpy(a,b,sizeof(b))#define rep(i,j,k) for(int i=(j);i<=(k);++i)#define per(i,j,k) for(int i=(j);i>=(k);--i)int read() { int a=0, f=1; char c=getchar(); while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); } while(isdigit(c)) a=a*10+c-'0', c=getchar(); return a*f;}const int N = 105;int n, m, a[N];signed main() { // ios::sync_with_stdio(0); // cin.tie(0); // cout.tie(0); cin >> n >> m; for(int i = 1;i <= m;i++) a[i] = i; for (int i = 1; i <= n; i++) { int x; cin >> x; vector<int> p; for(int j = 1;j <= x;j++) { int y; cin >> y; p.push_back(y); } int fg = 0; for(auto t : p) if(a[t]) { cout << t << endl; fg = 1; a[t] = 0; break; } if(!fg) cout << 0 << endl; } return 0;}C
抽象一下发现维护一个双端队列即可。我们先把每天买入的鸡蛋数量压入队列,然后不短取出队头来消耗。用不完的重新入队。这样每个 在每一天开始入队 1 次,被使用完后出队 1 次,用不完再放回去的次数,所有 均摊 次。
#include<bits/stdc++.h>using namespace std;typedef long long ll;typedef unsigned long long ull;typedef pair<int,int> PII;#define MP make_pair#define fi first#define se second#define pb push_back#define eb emplace_back#define SET(a,b) memset(a,b,sizeof(a))#define CPY(a,b) memcpy(a,b,sizeof(b))#define rep(i,j,k) for(int i=(j);i<=(k);++i)#define per(i,j,k) for(int i=(j);i>=(k);--i)int read() { int a=0, f=1; char c=getchar(); while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); } while(isdigit(c)) a=a*10+c-'0', c=getchar(); return a*f;}const int N = 2e5 + 5;int T, n, D, a[N], b[N];void solve() { cin >> n >> D; ll ans = 0; deque<PII > q; q.clear(); for(int i = 1; i <= n; i++) { cin >> a[i]; } for(int i = 1;i <= n;i++) { cin >> b[i]; } for(int i = 1;i <= n;i++) { q.push_back({a[i], i}); while(q.size() && q.front().second + D < i) { q.pop_front(); } int d = b[i]; while(q.size() && d) { PII x = q.front(); q.pop_front(); if(d >= x.first) { d -= x.first; continue; } else { q.push_front({x.first - d, x.second}); d = 0; } } } while(q.size()) { PII x = q.front(); q.pop_front(); if(x.second + D <= n) continue; ans += x.first; } cout << ans << endl;}signed main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> T; while(T--) solve(); return 0;}D
用std::map或者std::unordered_map就水过去了。
#include<bits/stdc++.h>using namespace std;typedef long long ll;typedef unsigned long long ull;typedef pair<int,int> PII;#define MP make_pair#define fi first#define se second#define pb push_back#define eb emplace_back#define SET(a,b) memset(a,b,sizeof(a))#define CPY(a,b) memcpy(a,b,sizeof(b))#define rep(i,j,k) for(int i=(j);i<=(k);++i)#define per(i,j,k) for(int i=(j);i>=(k);--i)int read() { int a=0, f=1; char c=getchar(); while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); } while(isdigit(c)) a=a*10+c-'0', c=getchar(); return a*f;}const int N = 2e5 + 5;int n, a[N];map<int, int> f;signed main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n; for(int i = 1;i <= n;i++) cin >> a[i]; f[a[1]] = 1; int ans = 1; for(int i = 2;i <= n;i++) { if(!f.count(a[i])) f[a[i]] = 1; if(!f.count(a[i] - 1)) continue; f[a[i]] = max(f[a[i]], f[a[i] - 1] + 1); ans = max(ans, f[a[i]]); } cout << ans << endl; return 0;}E
看起来是个很神秘的题。
在模 意义下考虑吧,这样数列中就不允许出现 。
对于 ,都会得到 。那么序列中这样的数对数量是平方的,可以预处理。
序列中出现 ,说明存在 或者 。由于数列无限递推,所以能到达它们的点就不允许出现其中,这就变成了一个可达性问题。对上述关系连边,得到一张挺稀疏的图。由于起点不固定,但终点(非法点)可以确定,所以建反边,从非法点开始搜,答案就是 减掉非法点所在的连通块大小之和。
#include<bits/stdc++.h>using namespace std;typedef long long ll;typedef unsigned long long ull;typedef pair<int,int> PII;#define MP make_pair#define fi first#define se second#define pb push_back#define eb emplace_back#define SET(a,b) memset(a,b,sizeof(a))#define CPY(a,b) memcpy(a,b,sizeof(b))#define rep(i,j,k) for(int i=(j);i<=(k);++i)#define per(i,j,k) for(int i=(j);i>=(k);--i)int read() { int a=0, f=1; char c=getchar(); while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); } while(isdigit(c)) a=a*10+c-'0', c=getchar(); return a*f;}const int N = 1e6 + 5;int n, A, B, res;bool v[N];vector<int> p[N];int id(int i, int j) { return i * n + j;}void dfs(int x) { v[x] = 1; res++; // cout << x << endl; // puts("Haha"); for(auto y : p[x]) if(!v[y]) { dfs(y); }}signed main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n >> A >> B; for(int x = 0; x < n; x++) { for(int y = 0; y < n; y++) { int ty = (A * y % n + B * x % n) % n; int tx = y; // 反向边 // printf("[%d, %d] -> [%d, %d]\n", x, y, tx, ty); p[id(tx, ty)].push_back(id(x, y)); } } for(int y = 0; y < n; y++) if(!v[id(0, y)]) { dfs(id(0, y)); } for(int x = 0; x < n; x++) if(!v[id(x, 0)]) { dfs(id(x, 0)); } cout << n * n - res << endl; return 0;}F
这题神了。
为了满足条件,必须要考虑 到 的路径,显然让最大编号最小化总是最优的,因为只考虑从 出发的可达性。能设 为从 出发到 ,且最小化路径最大节点编号,所对应的那个编号。
这样,由于我们要且只要到达 中所有节点,所以如果 ,那么必然经过编号更大的点,就无解了。
有
这个显然是有环的,不能直接做。考虑当前有一个已经确定的 ,由于最大值的性质,通过环不能将它更新得更小,所以可以放到Dijkstra上转移。
这样就判掉无解的情况了。
如果去想只到达 中节点需要删掉多少个点,信息就不太够,感觉找不到什么好做法。因为这时候容易求的最值类信息已经用不上了。
考虑一个点 什么时候应该被删除。
如果 中存在某个点 , 无论如何选择 到 的路径,一定能中途易辙走到 ,此时 必须删掉。
并且由于这个可达性要求是个前缀,如果点 能到达 ,那么对于 ,由于其一定要能到达 ,所以也一定能走到 。 所以一定存在一个节点 ,满足走到 中节点不需要删掉 ,但是走到 必须删。
于是问题就变成了如何求出这个 。
好像只要一个节点能够到达 就需要删掉 啊…
但这显然不是最优解,我们只需要断掉与 的路径,并不一定要删掉 。这就有了一种微妙的展开——对于每个 ,我们都求这个临界的 。具体的,考虑到达 的所有路径,这个 一定是路径上的某个点,同时,在考虑 中节点的时候,不需要删除 ,删除 即可保证不可达性。
于是这个 就是路径中不包含 的最小瓶颈编号。因为无法到达 ,也就无法前往 。同样其他到 的路径存在更大的编号,也一样不可达。
这样能避开无效删除吗?首先不会删得更多,因为冗余删除的情况就是本来已经不可到达 时删掉 ,我们考虑了所有路径,小于 时不存在走向 的路径,也就不会多删。会漏掉吗?显然更不会。
维护一个 为到达 所有路径中,不含节点 的最小瓶颈编号,求法与 类似。
影响范围是一个区间,差分维护即可。如果 ,就让 要删点的数量加上 。
复杂度
#include<bits/stdc++.h>using namespace std;typedef long long ll;typedef unsigned long long ull;typedef pair<int,int> PII;#define MP make_pair#define fi first#define se second#define pb push_back#define eb emplace_back#define SET(a,b) memset(a,b,sizeof(a))#define CPY(a,b) memcpy(a,b,sizeof(b))#define rep(i,j,k) for(int i=(j);i<=(k);++i)#define per(i,j,k) for(int i=(j);i>=(k);--i)int read() { int a=0, f=1; char c=getchar(); while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); } while(isdigit(c)) a=a*10+c-'0', c=getchar(); return a*f;}const int N = 3e5 + 5;int n, m, f[N], g[N], pre[N], ans[N];int c[N];bool v[N];vector<int> p[N], pq[N];void dijkstra() { priority_queue<int, vector<int>, greater<int> > q; for(int i = 1; i <= n; i++) f[i] = g[i] = n + 1; f[1] = 1; q.push(1); while(q.size()) { int x = q.top(); q.pop(); if(v[x]) continue; v[x] = 1; for(auto y : p[x]) { int t = f[y]; f[y] = min(f[y], max(f[x], y)); g[y] = min(g[y], max(f[x], x)); if(f[y] < t) { q.push(y); } } }}signed main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n >> m; for(int i = 1;i <= m;i++) { int x, y; cin >> x >> y; p[x].pb(y); pq[y].pb(x); } dijkstra(); for(int i = 1;i <= n;i++) { if(f[i] <= n && g[i] < i) { c[g[i]]++; c[i]--; } } pre[0] = 0; for(int i = 1;i <= n;i++) { c[i] += c[i - 1]; pre[i] = max(pre[i - 1], f[i]); if(pre[i] > i) cout << "-1" << endl; else cout << c[i] << endl; } return 0;}觉得这篇文章怎么样?
点个赞,让更多人看到!

评论区