为什么ABC那么多?建议Atcoder多出些ARC/AGC,好不容易才轮到AGC……
A
签到。就是以黑点为源点做多元最短路,由于边长是1直接bfs就好了,求最长路径。
#include<bits/stdc++.h> using namespace std; const int N=1007,dx[4]={0,0,1,-1},dy[4]={1,-1,0,0}; int n,m,ans,qs,qe,d[N][N],qx[N*N],qy[N*N]; char mp[N][N]; int main() { cin>>n>>m; for(int i=1;i<=n;i++)scanf("%s",mp[i]+1); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(mp[i][j]=='.')d[i][j]=1e9; else d[i][j]=0,qx[qe]=i,qy[qe++]=j; while(qs<qe) { int x=qx[qs],y=qy[qs++]; for(int i=0;i<4;i++) { int u=x+dx[i],v=y+dy[i]; if(u<1||u>n||v<1||v>m||d[u][v]<=d[x][y]+1)continue; d[u][v]=d[x][y]+1,ans=max(ans,d[u][v]); qx[qe]=u,qy[qe++]=v; } } cout<<ans<<endl; }
B
实际上上下和左右可以分开算。假设只有U和D,那么所有U/D操作结束后,若保留在棋盘,则在区间[L,R],于是根据是先手还是后手的操作,更改区间,若区间在操作途中为空或最终区间不包含棋子所在行/列,则先手获胜,反之后手获胜
#include<bits/stdc++.h> using namespace std; const int N=2e5+7; int n,m,p,sr,sc; char s[N],t[N]; bool walk1() { int L=1,R=n; for(int i=p;i;i--) { if(t[i]=='U')R=min(n,R+1);else if(t[i]=='D')L=max(1,L-1); if(s[i]=='U')L++;else if(s[i]=='D')R--; if(L>R)return 1; } return (sr<L||sr>R); } bool walk2() { int L=1,R=m; for(int i=p;i;i--) { if(t[i]=='L')R=min(m,R+1);else if(t[i]=='R')L=max(1,L-1); if(s[i]=='L')L++;else if(s[i]=='R')R--; if(L>R)return 1; } return (sc<L||sc>R); } int main() { scanf("%d%d%d%d%d",&n,&m,&p,&sr,&sc); scanf("%s",s+1); scanf("%s",t+1); if(walk1()||walk2())puts("NO"); else puts("YES"); }
C
发现每次操作对树的改变只有2种:1、删去所有叶节点(n=2时不可使用)。2、保留其中一个叶节点,删去其他点(n=1时不可使用)。开始瞎蒙个SG函数的错误结论上去,白掉5min。然后发现树等价于长为直径的链上游戏,于是DP链长度为i时先手是否必胜即可。
#include<bits/stdc++.h> using namespace std; const int N=2e5+7; int n,f[N],dep[N]; vector<int>G[N]; void dfs(int u,int fa) { for(int i=0;i<G[u].size();i++) if(G[u][i]!=fa)dep[G[u][i]]=dep[u]+1,dfs(G[u][i],u); } int main() { scanf("%d",&n); f[0]=1,f[1]=0; for(int i=2;i<=n;i++)if(f[i-1]&&f[i-2])f[i]=0;else f[i]=1; for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),G[x].push_back(y),G[y].push_back(x); dfs(1,0); int t=1; for(int i=2;i<=n;i++)if(dep[i]>dep[t])t=i; for(int i=1;i<=n;i++)dep[i]=0; dfs(t,0); t=0; for(int i=1;i<=n;i++)t=max(t,dep[i]); if(f[t])puts("First");else puts("Second"); }
D
很容易发现答案是O(logn)级别的,不妨考虑按照答案分步DP,设f[ans][i][j][k]表示答案<=ans,上下边界分别为i,j,左边界为k,右边界最靠右能达到多少。然后考虑转移,竖切可以直接转移,横切由于枚举横切点发现上下区间的DP值一个是先增后减,另一个是先减后增,然后可以二分转移。假设n,m同级,复杂度O(n3log2n)
#include<bits/stdc++.h> using namespace std; const int N=200; int n,m,f[2][N][N][N]; char s[N][N]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%s",s[i]+1); for(int i=n;i;i--) for(int j=i;j<=n;j++) for(int k=m;k;k--) if(i==j) { if(k==m)f[0][i][j][k]=m+1; else f[0][i][j][k]=s[i][k]==s[i][k+1]?f[0][i][j][k+1]:k+1; } else f[0][i][j][k]=s[i][k]==s[i+1][k]?min(f[0][i][i][k],f[0][i+1][j][k]):k; for(int ans=0,t=0;;ans++,t^=1) { if(f[t][1][n][1]==m+1){printf("%d ",ans);return 0;} for(int i=n;i;i--) for(int j=i;j<=n;j++) for(int k=m;k;k--) { f[t^1][i][j][k]=f[t][i][j][k]==m+1?m+1:f[t][i][j][f[t][i][j][k]]; if(i<j) { int l=i,r=j-1,mid; while(l<=r) { mid=l+r>>1; if(f[t][i][mid][k]<=f[t][mid+1][j][k]) f[t^1][i][j][k]=max(f[t^1][i][j][k],f[t][i][mid][k]),r=mid-1; else f[t^1][i][j][k]=max(f[t^1][i][j][k],f[t][mid+1][j][k]),l=mid+1; } } } } }
EF
咕
result:rank217 rating+=47 now_rating=1925