【题解】CF1213 合集
CF1213A Chips Moving
考虑是一道非常简单的入门题
就是奇数的个数和偶数的个数取 \(\min\) 即可
code
#include<bits/stdc++.h> using namespace std; const int NN = 108; int n; int a,cnt[2]; int main(){ scanf("%d",&n); for(int i = 1; i <= n; ++i){ scanf("%d",&a); ++cnt[a&1]; } printf("%d",min(cnt[0],cnt[1])); }
CF1213B Bad Prices
我们从后往前更新答案,每次维护后缀最小值即可
code
#include<bits/stdc++.h> using namespace std; const int NN = 2e5 + 8; int T,n; int a[NN]; int main(){ scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i = 1; i <= n; ++i) scanf("%d",&a[i]); int mn = 0x3f3f3f3f,ans = 0; for(int i = n; i >= 1; --i){ mn = min(a[i],mn); ans += mn != a[i]; } printf("%d\n",ans); } }
CF1213C Book Reading
这道题目,我们可以发现显然是有周期性的
我们个位数都可以统一成以 \(10\) 为一轮,然后就可以快速求解(预处理周期即可)
code
#include<bits/stdc++.h> using namespace std; typedef long long ll; int q; ll n,m; int a[10] = {0,45,40,45,40,25,40,45,40,45}; int main(){ scanf("%d",&q); for(int i = 1; i <= q; ++i){ ll ans = 0; scanf("%lld%lld",&n,&m); int base = m % 10; ll cnt = n / m; for(int i = 1; i <= cnt % 10; ++i){ ans += i * base % 10; } ans += cnt / 10 * a[base]; printf("%lld\n",ans); } }
CF1213D Equalizing by Division (EZ&HD)
标签:字符串
\(C^+\)
我们可以发现,最后剩下的数字一定是只剩下高位。
我们这道题可以建出一个 01trie
然后就可以进行求解。
我们按所有数转化为二进制后的长度从小到大推入 01trie
然后我们就可以对于每个节点记录前 \(k\) 个遍历到这个节点的全部将这个节点后面的数字去掉的代价
可以证明从小到大加入后前 \(k\) 个遍历到的一定是最优解之一
最后对于每个节点记录的值取 \(\min\) 即可
code
#include<bits/stdc++.h> using namespace std; const int NN = 2e5 + 8; int n,k; int a[NN]; string s[NN]; vector<int> ans; struct Trie{ int son[2]; int num,res; #define l(x) T[x].son[0] #define r(x) T[x].son[1] #define son(x,y) T[x].son[y-'0'] #define num(x) T[x].num #define res(x) T[x].res }T[NN << 4]; int cnt = 1; void insert(int p){ int now = 1; if(num(now) < k) ++num(now),res(now) += s[p].size(); for(int i = 0; i < s[p].size(); ++i){ if(son(now,s[p][i]) == 0){ son(now,s[p][i]) = ++cnt; } now = son(now,s[p][i]); if(num(now) < k){ ++num(now),res(now) += s[p].size() - i - 1; if(num(now) == k) ans.push_back(res(now)); } } } bool cmp(string &x,string &y){ return x.size() < y.size(); } int main(){ scanf("%d%d",&n,&k); for(int i = 1; i <= n; ++i) scanf("%d",&a[i]); for(int j = 1; j <= n; ++j){ if(a[j] == 0) s[j] = "0"; while(a[j]){ s[j] += (a[j] & 1) + '0'; a[j] >>= 1; } reverse(s[j].begin(),s[j].end()); } sort(s+1,s+1+n,cmp); for(int i = 1; i <= n; ++i) insert(i); sort(ans.begin(),ans.end()); printf("%d",ans[0]); }
CF1213E Two Small Strings
标签:思维题
\(C^-\)
考虑这道题目显然还是很简单
我们考虑构造,很容易想到一种方法就是找到一个 \(abc\) 的排列,最后重复 \(n\) 次
但是我们会发现这样一组数据:
3
ab
ac
我们显然对于上面的一组数据就不能这样构造。
但是我们有另外一组构造方法:\(bbbcccaaa\)
就是对于 \(bca\) 这个排列来说,每个字母重复 \(n\) 遍即可
最后将两个构造方法合并即可通过本题。
code
#include<bits/stdc++.h> using namespace std; bool a[3][3]; int n; char s[10]; int main(){ memset(a,1,sizeof(a)); scanf("%d",&n); for(int i = 1; i <= 2; ++i){ scanf("%s",s); a[s[0]-'a'][s[1]-'a'] = 0; } if(a[0][0] && a[1][1] && a[2][2]){ for(int i = 0; i <= 2; ++i){ for(int j = 0; j <= 2; ++j){ if(!a[i][j] || i == j) continue; for(int k = 0; k <= 2; ++k){ if(!a[j][k] || j == k || i == k) continue; puts("YES"); for(int l = 1; l <= n; ++l) printf("%c",i+'a'); for(int l = 1; l <= n; ++l) printf("%c",j+'a'); for(int l = 1; l <= n; ++l) printf("%c",k+'a'); return 0; } } } } else{ for(int i = 0; i <= 2; ++i){ for(int j = 0; j <= 2; ++j){ if(!a[i][j] || i == j) continue; for(int k = 0; k <= 2; ++k){ if(!a[j][k] || j == k || i == k || !a[k][i]) continue; puts("YES"); for(int l = 1; l <= n; ++l) printf("%c%c%c",i+'a',j+'a',k+'a'); return 0; } } } } }
CF1213F Unstable String Sort
标签:图论
\(C\)
你考虑我们可以发现,排列的限制本质上是 \(s_{a_i} \leq s_{a_{i+1}} \leq s_{a_i}+1\)
其实这个建出来就可以去跑 差分约束
了(读者可以尝试一下)
我们又可以发现,因为有字母种类的限制 \(k\),但是显然我们构造了一个种类大于 \(k\) 的方案可以很容易的将种类缩减为 \(k\)
所以现在只需要让种类数量越大越好。
我们发现如果成环的话,显然环上点的种类必须相同。
最后我们缩点完了就直接跑一边 DFS
即可
code
#include<bits/stdc++.h> using namespace std; const int NN = 2e5 + 8; int n,k; struct Graph{ struct Edge{ int to,next; }edge[NN << 1]; int head[NN],cnt; void init(){ memset(head,-1,sizeof(head)); cnt = 1; } void add_edge(int u,int v){ edge[++cnt] = {v,head[u]}; head[u] = cnt; } }G,T; bool vis[NN]; int sta[NN],top; int dfn[NN],low[NN],timet; int scc[NN],scccnt; void tarjan(int u){ dfn[u] = low[u] = ++timet; sta[++top] = u;vis[u] = 1; for(int i = G.head[u]; i != -1; i = G.edge[i].next){ int v = G.edge[i].to; if(!dfn[v]){ tarjan(v); low[u] = min(low[u],low[v]); } else if(vis[v]) low[u] = min(low[u],dfn[v]); } if(dfn[u] == low[u]){ ++scccnt; while(sta[top+1] != u){ scc[sta[top]] = scccnt; vis[sta[top]] = 0; --top; } } } int du[NN]; int dis[NN]; int dismax = 0; void tope(){ queue<int> q; for(int i = 1; i <= scccnt; ++i){ if(du[i] == 0) q.push(i); } while(!q.empty()){ int u = q.front();q.pop(); for(int i = T.head[u]; i != -1; i = T.edge[i].next){ int v = T.edge[i].to; dis[v] = max(dis[v],dis[u]+1); if(--du[v] == 0) q.push(v); dismax = max(dismax,dis[v]); } } } int main(){ G.init();T.init(); scanf("%d%d",&n,&k); for(int i = 1,a,pre; i <= n; ++i){ scanf("%d",&a); if(i != 1) G.add_edge(pre,a); pre = a; } for(int i = 1,a,pre; i <= n; ++i){ scanf("%d",&a); if(i != 1) G.add_edge(pre,a); pre = a; } for(int i = 1; i <= n; ++i){ if(!dfn[i])tarjan(i); } for(int u = 1; u <= n; ++u){ for(int i = G.head[u]; i != -1; i = G.edge[i].next){ int v = G.edge[i].to; if(scc[u] == scc[v]) continue; T.add_edge(scc[u],scc[v]); ++du[scc[v]]; } } tope(); if(dismax < k-1){ puts("NO"); return 0; } puts("YES"); for(int i = 1; i <= n; ++i){ printf("%c",'a' + min(k-1,dis[scc[i]])); } }
CF1213G Path Queries
我们这道题显然第一步就是离线询问,从小到大加边
我们显然需要动态维护联通块的大小来维护答案
进而可以想到 带权并查集
即可
code
#include<bits/stdc++.h> using namespace std; const int NN = 2e5 + 8; typedef long long ll; int n,m; struct E{ int u,v; int val; bool operator < (const E &x)const{ return val < x.val; } }e[NN]; int fa[NN],siz[NN],cnt; ll ans[NN]; ll res; int find(int x){ return x == fa[x] ? x : fa[x] = find(fa[x]); } void merge(int x,int y){ int fx = find(x),fy = find(y); if(fx == fy) return; res -= 1ll * siz[fx] * (siz[fx]-1) / 2; res -= 1ll * siz[fy] * (siz[fy]-1) / 2; fa[fx] = fy; siz[fy] += siz[fx]; res += 1ll * siz[fy] * (siz[fy]-1) / 2; return ; } int main(){ scanf("%d%d",&n,&m); for(int i = 1; i <= n; ++i) fa[i] = i,siz[i] = 1; for(int i = 1; i < n; ++i){ scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].val); } sort(e+1,e+1+n); int now = 1; for(int i = 1; i <= 2e5; ++i){ for(; now <= n && e[now].val <= i; ++now){ merge(e[now].u,e[now].v); } ans[i] = res; } for(int i = 1; i <= m; ++i){ int x; scanf("%d",&x); printf("%lld ",ans[x]); } }
本文来自博客园,作者:ricky_lin,转载请注明原文链接:https://www.cnblogs.com/rickylin/p/CF1213.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步