隐藏页面特效

AtCoder Grand Contest 041

1|0Preface


花了两个晚自习加上一个中午和一个周末晚上和大量的自习课时间打完了

ABE还勉强算自己想的都是陈指导想的,C看了点提示在同学的帮助下Rush出来了,D只能自己想出O(n3)暴力,F想了两天连状态都不知道怎么设看了题解直呼内行

总结:水平太次,建议回炉重造


2|0A - Table Tennis Training


签到题,注意到如果两个人之间的位置差是偶数的话直接走就好了

否则就是两种情况:

  1. 在最左边或最右边相遇(先到的人可以一直等后面的人到)
  2. 在左边的人到了最左边之后等待一轮,然后在向右走与右边的人碰头;右边的人同理

讨论一下取最小值即可,WA了一发没看见要开long long

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; long long n,a,b; int main() { scanf("%lld%lld%lld",&n,&a,&b); if ((b-a)%2==0) return printf("%lld",(b-a)/2),0; long long res=min(b-1,n-a); res=min(res,min((a+b)/2,(2*n-a-b+1)/2)); return printf("%lld",res),0; }

3|0B - Voting Judges


简单分析题。首先我们发现给ai从大到小排序后前p个数是一定合法的(废话)

然后考虑从p+1开始枚举最小的能被加到前p的位置i,贪心地想我们肯定只要加到第p个地方即可

考虑这个位置每次都被加1,那么剩下的v1次怎么分配?

显然对于所有位置在它之后以及前p1个数都可以随便加,那么我们给这些数也加满

那么问题来了,如果操作次数还有多怎么办,只能填在[p+1,i)之间了

考虑判断不合法时怎么操作,我们求出S=j=p+1i1aj,然后判断(ai+m)×(ip)S与剩下来的增加次数的关系

为什么可以这么做呢,因为[p+1,i)中的数都ai,那么每个数都是可以经过m次操作超过ai+m的,那么极限情况就是全加到ai+m

时间复杂度O(nlogn),因为要排序。然后有一个坑点,要判断ai+map,刚开始石乐志没判WA了两发

#include<cstdio> #include<algorithm> #define RI register int #define CI const int& using namespace std; const int N=100005; int n,a[N],m,v,p; long long sum[N]; inline bool cmp(CI x,CI y) { return x>y; } int main() { RI i; for (scanf("%d%d%d%d",&n,&m,&v,&p),i=1;i<=n;++i) scanf("%d",&a[i]); for (sort(a+1,a+n+1,cmp),i=1;i<=n;++i) sum[i]=sum[i-1]+a[i]; for (i=p+1;i<=n;++i) { int dlt=max(0,v-(p+n-i)); long long tot=sum[i-1]-sum[p-1]; if (a[i]+m<a[p]||1LL*(i-p)*(a[i]+m)+1-tot<=1LL*dlt*m) return printf("%d",i-1),0; } return printf("%d",n),0; }

4|0C - Domino Quality


手玩构造题。这种题目看完就想着先手玩小的看看规律

然后和陈指导Rush了快1hour然后只搞出了n=3,4,5的情况,感觉没什么规律

瞄了一眼题解,原来n是偶数可以构造,回班之后开始尝试,竟然很快就Rush出来了(自习课加智商),以n=8为例(一看就知道怎么构造的说):

aa....cd bb....cd cdaa.... cdbb.... ..cdaa.. ..cdbb.. ....cdaa ....cdbb

同时也容易想到n|3时可以直接用3×3的拼出来,那么现在就考虑n| 3的奇数,考虑因为我们偶数的构造法行列都是三个,然后n=5时的答案:

aabc. ..bcd fee.d f.ghh iigjj

也是行列三个,容易想到我们先把左上角填一个5×5的,然后在右下角填一个(n5)×(n5)

然后大功告成?发现当n=7的时候就GG了,那怎么办?

手玩构造n=7的答案(感谢无私奉献的同桌):

ab...c. ab...c. ..dee.f ..d.g.f ..hhg.i jj..kki llmm.nn

然后终于做完了,WA了一发在拼图的时候把偶数的最后一块填到第一行去了。。。

#include<cstdio> #define RI register int #define CI const int& using namespace std; const int N=1005; char tp5[N][N]={ "aabc.", "..bcd", "fee.d", "f.ghh", "iigjj" }, tp7[N][N]={ "ab...c.", "ab...c.", "..dee.f", "..d.g.f", "..hhg.i", "jj..kki", "llmm.nn" }; int n; char a[N][N]; inline void odd_paint(CI sx=0) { for (RI i=sx;i<n;i+=2) a[i][i]=a[i][i+1]='a',a[i+1>=n?i+1-n+sx:i+1][i]=a[i+1>=n?i+1-n+sx:i+1][i+1]='b', a[i+2>=n?i+2-n+sx:i+2][i]=a[i+3>=n?i+3-n+sx:i+3][i]='c',a[i+2>=n?i+2-n+sx:i+2][i+1]=a[i+3>=n?i+3-n+sx:i+3][i+1]='d'; } inline void print(CI n,char c[N][N]) { for (RI i=0;i<n;++i) { for (RI j=0;j<n;++j) putchar(c[i][j]); if (i!=n-1) putchar('\n'); } } int main() { RI i,j; scanf("%d",&n); if (n==2) return puts("-1"),0; if (n==5) return print(5,tp5),0; if (n==7) return print(7,tp7),0; for (i=0;i<n;++i) for (j=0;j<n;++j) a[i][j]='.'; if (n%3==0) { for (i=0;i<n;i+=3) for (j=0;j<n;j+=3) a[i][j]=a[i][j+1]='a',a[i+1][j+2]=a[i+2][j+2]='b'; return print(n,a),0; } if (n%2==0) return odd_paint(),print(n,a),0; for (i=0;i<5;++i) for (j=0;j<5;++j) a[i][j]=tp5[i][j]; return odd_paint(5),print(n,a),0; }

5|0D - Problem Scores


Rush了C之后看了看DE题面就滚回班了,连着想了两天然后只出了一个O(n4)(然后被陈指导指点了一下复杂度发现是O(n3)的)

首先我们考虑直接令ai增序,然后记Si=j=1iaj,那么题中的条件等价于:

k(1,n1),Sk>SnSnk+1Sk+Snk+1>Sn

显然由于Sk+Snk+1的对称性,我们只要做一半即可,换句话说只要前n+12满足条件即可

然后我们考虑给S展开然后移个项,就有Sk+Snk+1>Sna1i=2k(ani+2ai)>0

然后我们观察这个式子,当kn+12时,ani+2ai0,说明左边的值单调不增

同时根据题设,ani+2ai也是单调不增的,因此容易发现我们可以把两维都压到状态里

fi,j,k表示前i个数,a1i=2k(ani+2ai)=jani+2ai=k的方案数,然后转移的时候枚举上一次的差值t,容易得到转移:

fi,j,k=tkfi1,j+k,t×(tk+1)=tkfi1,j+k,t×ttkfi1,j+k,t×(k1)

直接维护两个数组记录上面两个项的后缀和然后减一减即可,注意下边界

然后我们发现n是奇数的时候中间的数要特别考虑,还要在枚举转移方式讨论一下(实现详见代码)

然后这个暴力DP状态O(n3)转移O(1),因此复杂度O(n3)

当然如果你想伟大的陈指导一样压一下上界的话,复杂度是O(n2logn)

暴力的代码:

#include<cstdio> #define RI register int #define CI const int& const int N=5005; int n,mod,f[N][N],g[N][N],c[N][N],ans; // f[x][y] x:sum y:last delta inline void inc(int& x,CI y) { if ((x+=y)>=mod) x-=mod; } inline int sum(CI x,CI y) { int t=x+y; return t>=mod?t-mod:t; } int main() { RI i,j,k; for (scanf("%d%d",&n,&mod),i=1;i<=n;++i) f[i][n-i]=1; for (j=1;j<=n;++j) for (k=n;~k;--k) g[j][k]=sum(g[j][k+1],f[j][k]); for (j=1;j<=n;++j) for (k=n;~k;--k) c[j][k]=sum(c[j][k+1],1LL*f[j][k]*k%mod); for (i=2;i<=1+(n-1)/2;++i) { for (j=1;j<=n;++j) for (k=0;k<=n;++k) f[j][k]=0; for (j=1;j<=n;++j) for (k=0;k<=n-1;++k) if (j+k<=n) inc(f[j][k],sum(c[j+k][k],(mod-1LL*g[j+k][k]*(k-1)%mod)%mod)); for (j=1;j<=n;++j) for (k=0;k<=n;++k) g[j][k]=c[j][k]=0; for (j=1;j<=n;++j) for (k=n;~k;--k) g[j][k]=sum(g[j][k+1],f[j][k]); for (j=1;j<=n;++j) for (k=n;~k;--k) c[j][k]=sum(c[j][k+1],1LL*f[j][k]*k%mod); } for (i=1;i<=n;++i) for (j=0;j<=n;++j) inc(ans,1LL*(n&1?1:j+1)*f[i][j]%mod); return printf("%d",ans),0; }

然后这样的DP肯定就是考虑如何去掉其中的一维,而我和陈指导一直都以为要去掉和的那一维,然后笋干爆炸

考虑进一步转化问题,我们设xi=ani+2ai,特殊地x1=na1

然后现在问题变成了xi[0,n),xixi+1,求i=2k(xi1xi+1)(结合前面DP的转移系数看一下就能发现只是把问题变了个形式)

考虑我们还是依次枚举xi,由于xixj,j[1,i),我们可以把所有的xj都减去xi(因为不影响差值),即

xixj,j[1,i),i=2k(xi1xi+1)0xj,j[1,i),i=2k(xi1xi+1)

容易发现现在的xj和之前定义的取值范围相同了,也就是说他可以看做原问题的子问题,我们可以用一个状态一起表示

所以设fi,j表示前ixik=1ixk=jk=2i(xk1xk+1),然后我们只需要枚举当前的xi即可转移

但是虽然DP好写了但是还是O(n3)的啊,不过对于这个DP我们很容易优化(据说是常见技巧)

考虑xk1xk+1相当于在[xk,xk1]中选出一个点(姑且叫做关键点)的方案数

然后我们可以不用枚举当前的xi的值,加一维0/1表示当前是否选择了关键点,然后就可以把许多状态同时按序转移了

具体详见代码,复杂度O(n2)

#include<cstdio> #define RI register int #define CI const int& const int N=5005; int n,m,mod,f[N][N][2],ans; // f[x][y] x:sum y:last delta inline void inc(int& x,CI y) { if ((x+=y)>=mod) x-=mod; } inline int sum(CI x,CI y) { int t=x+y; return t>=mod?t-mod:t; } int main() { RI i,j; scanf("%d%d",&n,&mod); m=(n-1)/2; for (f[0][0][0]=i=1;i<=m;++i) for (j=0;j<=n;++j) { f[i][j][1]=f[i-1][j][0]; if (i<=j) inc(f[i][j][1],f[i][j-i][1]); f[i][j][0]=f[i][j][1]; if (i<=j) inc(f[i][j][0],f[i][j-i][0]); } if (n&1) { for (i=1;i<=n;++i) for (j=0;n-i-j*(m+1)>=0;++j) inc(ans,f[m][n-i-j*(m+1)][0]); } else { for (i=1;i<=n;++i) for (j=0;n-i-j*(m+1)>=0;++j) inc(ans,1LL*f[m][n-i-j*(m+1)][0]*(j+1)%mod); } return printf("%d",ans),0; }

6|0E - Balancing Network


E题小清新乱搞题,感觉比D简单多了

首先考虑T=1,容易想到如果我们枚举最后走到的电线i

假设每条电线都有机会走到i,考虑可以从后往前处理每条路径构造答案

如果路径两端都能到i或者都到不了i显然就可以随便放,否则让路径从不能到的指向能到的即可

那么怎么判断每条电线能否被其他电线走到呢,很简单直接把路径先当成双向的然后bitset搞一波即可

这部分就轻松做完了,复杂度O(nmw)

然后考虑T=2,我们还是一样的从后往前处理路径,记录一个tari表示电线i最后走到的电线是哪条

显然如果我们把路径定向成xy那么tarx=tary

由于要让最后所有的tari不全相同,那么我们尽量每次让tari出现次数多的变成出现次数少的,不难证明这样是可以满足要求了

然后特殊情况是n=2,此时无论如何都会走到一起,特判即可,一发入魂

#include<cstdio> #include<bitset> #define RI register int #define CI const int& using namespace std; const int N=50005; int n,m,tp,x[N<<1],y[N<<1]; char s[N<<1]; namespace Case1 { bitset <N> vis[N]; bool c[N]; inline void solve(void) { RI i,j; for (i=1;i<=n;++i) vis[i].set(i); for (i=1;i<=m;++i) vis[x[i]]=vis[y[i]]=vis[x[i]]|vis[y[i]]; for (i=1;i<=n;++i) if (vis[i].count()==n) { for (c[i]=1,j=m;j;--j) if (c[x[j]]) s[j]='^',c[y[j]]=1; else if (c[y[j]]) s[j]='v',c[x[j]]=1; else s[j]='^'; return (void)(puts(s+1)); } puts("-1"); } }; namespace Case2 { int tar[N],c[N]; inline void solve(void) { if (n<=2) return (void)(puts("-1")); RI i; for (i=1;i<=n;++i) c[tar[i]=i]=1; for (i=m;i;--i) if (c[tar[x[i]]]>=c[tar[y[i]]]) s[i]='v',--c[tar[x[i]]],++c[tar[x[i]]=tar[y[i]]]; else s[i]='^',--c[tar[y[i]]],++c[tar[y[i]]=tar[x[i]]]; puts(s+1); } }; int main() { RI i; for (scanf("%d%d%d",&n,&m,&tp),i=1;i<=m;++i) scanf("%d%d",&x[i],&y[i]); if (tp==1) Case1::solve(); else Case2::solve(); return 0; }

7|0F - Histogram Rooks


想的时候完全没有思路的说,ORZcz_xuyixuan聚聚

这类计数DP最难的地方就在于状态的设计,如何设计一盒好的状态不重不漏同时复杂度够低

考虑对于每一列的状态,有以下三种可能:

  1. 这一列有车
  2. 这一列没车,但是每行都被其他列的车控制了
  3. 这一列没车,而且存在没有被控制的行

然后考虑关键的一点,假设我们当前只考虑这一列的上面的一些行,那么如果下面的那些行中每行都被控制了,那么(2)类列可以被视为(1)类列;否则(2)类列被视为(3)类列

那么我们在状态中可以把某些列的上面h行压入状态,同时加上一维0/1表示在下面的行中是否存在没有车的行,然后把(3)类列的个数记录进去即可

转移的话由于需要逐行转移,由于这里的图高度参差不齐,因此每次可以移除最下面的一行或者断开已经空了的列处理子图

乍一看状态很多,实际上由于只会被断开n次,状态数仍然是O(n3)级别的,转移的话讨论放这一列或者不放即可,代码中注释比较详尽

记忆化搜索实现即可,转移的时候类似树上背包是不会影响最终的复杂度的

#include<cstdio> #define RI register int #define CI const int& using namespace std; const int N=405,mod=998244353; int n,a[N],f[N<<1][N][N][2],pw2[N],C[N][N],id[N][N],size[N<<1],tot; //f[i][j][k][0/1] i:state j:height k:unfilled number 0/1 is under finished? inline void inc(int& x,CI y) { if ((x+=y)>=mod) x-=mod; } inline int DP(CI l=1,CI r=n,CI h=0) { if (l>r) return f[0][h][0][0]=f[0][h][0][1]=1,0; int &now=id[l][r],pos=l; RI i,j; if (!now) now=++tot; for (i=l+1;i<=r;++i) if (a[i]<a[pos]) pos=i; if (a[pos]==h) //remove the lowest column { int x=DP(l,pos-1,h),y=DP(pos+1,r,h); size[now]=size[x]+size[y]+1; //size calculated the number of column for (i=0;i<=size[x];++i) for (j=0;j<=size[y];++j) inc(f[now][h][i+j][0],1LL*f[x][h][i][0]*f[y][h][j][0]%mod), // if draw inc(f[now][h][i+j+1][1],1LL*f[x][h][i][1]*f[y][h][j][1]%mod); //or so } else //remove the lowest row { for (DP(l,r,h+1),i=0;i<=size[now];++i) inc(f[now][h][i][0],f[now][h+1][i][1]),inc(f[now][h][i][1],f[now][h+1][i][1]),//filled draw or not inc(f[now][h][i][0],1LL*f[now][h+1][i][0]*(pw2[size[now]-i]-1)%mod), //draw i columns and fill other size[now]-i columns (can't all empty) inc(f[now][h][i][1],1LL*f[now][h+1][i][1]*(pw2[size[now]-i]-1)%mod); for (i=1;i<=size[now];++i) for (j=1;j<=i;++j) inc(f[now][h][i-j][0],1LL*f[now][h+1][i][0]*pw2[size[now]-i]%mod*C[i][j]%mod), //choose j columns from i filled columns, other columns are filled optionally inc(f[now][h][i-j][1],1LL*f[now][h+1][i][1]*pw2[size[now]-i]%mod*C[i][j]%mod);//same as above } return now; } int main() { RI i,j; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]); for (pw2[0]=i=1;i<=n;++i) pw2[i]=2LL*pw2[i-1]%mod; for (C[0][0]=i=1;i<=n;++i) for (C[i][0]=j=1;j<=n;++j) C[i][j]=C[i-1][j-1],inc(C[i][j],C[i-1][j]); int now=DP(); return printf("%d",f[now][0][0][0]),0; }

8|0Postscript


一周做一场AGC再写个题解就差不多的说,其他题目都没什么时间去写了233


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/12902274.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(272)  评论(1编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
历史上的今天:
2018-05-16 Luogu P3390 【模板】矩阵快速幂&&P1939 【模板】矩阵加速(数列)
点击右上角即可分享
微信分享提示