隐藏页面特效

Codeforces Round 858 (Div. 2)

1|0Preface


这场CF打的好难受啊,A刚开始忘记切换语言了CE了两发,B漏了一种情况挂了一发,C纯靠暴力找性质,D比赛时没人写我题目都没仔细看

然后E本来秒出了解法,结果就是因为unordered_map的大常数一直TLE,而且由于我又菜又懒没去手写Hash

最后综合下来就是大爆炸,苦路西苦路西


2|0A. Walking Master


首先特判掉一些显然无解的情况,然后我们先把(a,b)修到(c,t)

然后若t>d则显然无解,否则我们可以通过调用两次操作来使得t+1直到其等于d

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; int t,a,b,c,d; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { scanf("%d%d%d%d",&a,&b,&c,&d); int ans=0; if (b>d) { puts("-1"); continue; } if (a<c) b+=c-a,ans=c-a,a=c; else ans=a-c; if (b>d) puts("-1"); else printf("%d\n",2*(d-b)+ans); } return 0; }

3|0B. Mex Master


分类讨论题想清楚了再交啊!!!

由于是要求mex的最小,因此首先考虑统计出0的个数c0,显然若c0nc0+1,则我们总可以构造一种方案使得相邻两数之和不为0,此时的答案就是0

否则考虑若数列中存在大于1的数,则显然我们可以把数列前面一段都置为0,然后在后面放一个大于1的数,此时任意相邻两数之和要么大于1要么等于0,此时的答案就是1

否则则说明数列中只含有01,此时不论如何构造答案都只能是2

但是要注意特判所有数都等于0的情况

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=200005; int t,n,a[N],c0,mx; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i; for (scanf("%d",&n),mx=c0=0,i=1;i<=n;++i) scanf("%d",&a[i]),c0+=a[i]==0,mx=max(mx,a[i]); if (c0==n) { puts("1"); continue; } if (c0<=n-c0+1) { puts("0"); continue; } if (mx>1) puts("1"); else puts("2"); } return 0; }

4|0C. Sequence Master


纯在找规律,不然真的想半天连样例的第三组和第四组是怎么构造出来的都想不通

先扔一个比赛时暴力找规律的代码,虽然跑的有点慢不过基本能通过它找到性质

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=200005,M=20; int n=4,a[10]; inline void DFS(CI now=1) { if (now>n) { bool flag=1; for (RI i=0;i<(1<<n);++i) { RI j; int c=0; for (j=0;j<n;++j) c+=((i>>j)&1); if (c!=n/2) continue; int prod=1,sum=0; for (j=0;j<n;++j) if ((i>>j)&1) prod*=a[j+1]; else sum+=a[j+1]; if (prod!=sum) { flag=0; break; } } if (flag) for (RI i=1;i<=n;++i) printf("%d%c",a[i]," \n"[i==n]); return; } for (RI i=-M;i<=M;++i) a[now]=i,DFS(now+1); } int main() { freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); return DFS(),0; }

然后大概总结一下规律就是(默认全为0总是一种可行解,且以下的数列不考虑顺序):

  • n=1时,答案为|a1a2|
  • n=2时,有以下两种其它的合法序列:
    • [2,2,2,2]
    • [2,1,11]
  • n为大于3的奇数时,此时不存在除了全为0以外的合法解
  • n为大于2的偶数时,此时还存在如下的构造方案:
    • [n,1,1,1,,1](即一个n2n11

然后讨论下原序列到哪个目标状态的变化值最小即可

#include<cstdio> #include<iostream> #include<algorithm> #define RI register int #define CI const int& using namespace std; const int N=400005; int t,n,a[N]; long long ans; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i,j; for (scanf("%d",&n),ans=0,i=1;i<=n*2;++i) scanf("%d",&a[i]),ans+=abs(a[i]); if (n==1) { printf("%lld\n",1LL*abs(a[1]-a[2])); continue; } if (n==2) { long long ret=0; for (i=1;i<=4;++i) ret+=abs(a[i]-2); ans=min(ans,ret); for (i=1;i<=4;++i) { for (ret=abs(a[i]-2),j=1;j<=4;++j) if (i!=j) ret+=abs(a[j]+1); ans=min(ans,ret); } printf("%lld\n",ans); continue; } if (n&1) printf("%lld\n",ans); else { long long tot=0; for (i=1;i<=n*2;++i) tot+=abs(a[i]+1); for (i=1;i<=n*2;++i) ans=min(ans,tot-abs(a[i]+1)+abs(a[i]-n)); printf("%lld\n",ans); } } return 0; }

5|0D. DSU Master


比赛时纯题目都没看,今天想了下还是屁都想不出来,只能去看Tutorial,只能说好仙好仙

我们考虑新加入一条边(i,i+1)对答案的影响,不难发现此时1必须是1n组成的弱联通分量里出度为0的点,否则(i,i+1)这条边一定无法产生贡献

除此之外,还必须满足(i,i+1)这条边必须是最后才加入的,并且此时ai=0

因此我们可以写出一个关于答案的转移方程,ansi=ansi1×i+[ai=0]×fi

因为从i1转移到i(i,i+1)这条边不管插入在哪个位置都不会影响i1的贡献,因此要乘上i

fi表示1n组成的弱联通分量的出度为0的点为1的方案数,有转移fi+1=fi×(iai)

这个转移方程的推导也很trivial,若ai=0则这条边(i,i+1)随便怎么连,方案数就是fi×i

否则若ai=1则这条边(i,i+1)不能放在最后,不然就会引出一条从1i+1的边,因此转移系数要减去1

综上我们即可O(n)递推解决该问题

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=500005,mod=998244353; int t,n,a[N],f[N],ans; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { RI i; for (scanf("%d",&n),i=1;i<n;++i) scanf("%d",&a[i]); for (f[1]=i=1;i<n;++i) f[i+1]=1LL*f[i]*(i-a[i])%mod; for (ans=0,i=1;i<n;++i) printf("%d%c",ans=(1LL*ans*i+f[i]*(1-a[i]))%mod," \n"[i==n-1]); } return 0; }

6|0E. Tree Master


这题基本一眼没有什么优秀的做法,暴力跳的部分感觉没有优化空间了,那么不妨大胆以暴力为基础构思

首先对于询问的两个点x,y,我们先找到它们的LCA,记为z, 然后考虑利用类似于根号分治的思想:

  • depxdepzn,则我们直接暴力把x,y跳到z,然后加上提前预处理好的根节点到每个点的答案,这部分复杂度是O(nn)
  • depxdepz>n,不妨设depxdepz=t>n,则这样的点最多有nt个,两两配对最多只有n2t2对,如果我们把每次跳的点对的信息都记忆化一下,总复杂度的上界就是n2t2×t=n2t,当t>n时这部分复杂度也是O(nn)

然后我写的时候在记忆化的地方直接开个unordered_map,然后常数有点大直接爆炸了

后来发现由于n105,可以调整下阈值来减少对记忆化部分的调用,遂把阈值改到500即可通过

#include<cstdio> #include<iostream> #include<algorithm> #include<vector> #include<unordered_map> #include<utility> #define RI register int #define CI const int& using namespace std; const int N=100005,S=500; int t,n,q,a[N],x,y,anc[N][20],dep[N]; long long pfx[N]; vector <int> v[N]; unordered_map <int,long long> hsh[N]; inline void DFS(CI now=1,CI fa=0) { RI i; pfx[now]=pfx[fa]+1LL*a[now]*a[now]; dep[now]=dep[anc[now][0]=fa]+1; for (i=0;i<19;++i) if (anc[now][i]) anc[now][i+1]=anc[anc[now][i]][i]; else break; for (int to:v[now]) if (to!=fa) DFS(to,now); } inline int getLCA(int x,int y) { if (dep[x]<dep[y]) swap(x,y); for (RI i=19;~i;--i) if (dep[anc[x][i]]>=dep[y]) x=anc[x][i]; if (x==y) return x; for (RI i=19;~i;--i) if (anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i]; return anc[x][0]; } inline long long jump(int x,int y,CI fa,long long ret=0) { if (x==fa) return pfx[fa]; if (x>y) swap(x,y); if (dep[x]-dep[fa]<=S) return jump(anc[x][0],anc[y][0],fa)+1LL*a[x]*a[y]; if (hsh[x].count(y)) return hsh[x][y]; return hsh[x][y]=jump(anc[x][0],anc[y][0],fa)+1LL*a[x]*a[y]; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i; for (scanf("%d%d",&n,&q),i=1;i<=n;++i) scanf("%d",&a[i]); for (i=2;i<=n;++i) scanf("%d",&x),v[x].push_back(i); for (DFS(),i=1;i<=q;++i) scanf("%d%d",&x,&y),printf("%lld\n",jump(x,y,getLCA(x,y))); return 0; }

然后看了Tutorial后发现其实可以只对每种深度的节点数小于n的某些点做记忆化

这样只要开个数组就好了,而且还不用写LCA了,最后跑起来也是飞快

#include<cstdio> #include<iostream> #include<algorithm> #include<vector> #include<unordered_map> #include<utility> #define RI register int #define CI const int& using namespace std; const int N=100005,S=340; int t,n,q,a[N],x,y,dep[N],id[N],c[N],fa[N]; long long hsh[N][S+5]; inline long long solve(int x,int y) { if (!x) return 0; if (x>y) swap(x,y); if (id[y]<=S&&hsh[x][id[y]]) return hsh[x][id[y]]; long long tmp=solve(fa[x],fa[y])+1LL*a[x]*a[y]; if (id[y]<=S) hsh[x][id[y]]=tmp; return tmp; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i; for (scanf("%d%d",&n,&q),i=1;i<=n;++i) scanf("%d",&a[i]); for (i=2;i<=n;++i) scanf("%d",&fa[i]),dep[i]=dep[fa[i]]+1; for (i=1;i<=n;++i) id[i]=++c[dep[i]]; for (i=1;i<=q;++i) scanf("%d%d",&x,&y),printf("%lld\n",solve(x,y)); return 0; }

7|0Postscript


最后的F1/F2感觉有点仙啊,周末过完了就先放一放了

希望下场别遇到这种纯在坐牢的场次了,感觉比赛的时候都没有太多好的idea,只是在找规律和卡时间的说


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/17233974.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(134)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示