隐藏页面特效

AtCoder Regular Contest 149

1|0Preface


最近国庆在外面玩的有点high啊,欠了一篇AT和两篇CF没写,今天先浅浅写一下这场

当时10.2号在外面玩得有点晚了,到寝室刚好比赛开始,晚饭都没吃就开干了

主要是C写的太久了,而且挂了一发之后看了好久才发现n=3的情形没处理好

然后40min做D没做出来,鉴定为纯纯的FW(话说原来ARC这么难的吗,感觉比以前的一些AGC还难)


2|0A - Repdigit Number


送分题,暴力枚举每一种情况判断即可

#include<cstdio> #define RI register int #define CI const int& using namespace std; int n,m,md,mn=-1; inline void print(CI d,CI n) { for (RI i=1;i<=n;++i) putchar(d+'0'); } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i,j; for (scanf("%d%d",&n,&m),j=1;j<=9;++j) { int cur=0; for (i=1;i<=n;++i) if ((cur=(10LL*cur+j)%m)==0) { if (i>mn||(i==mn&&j>md)) mn=i,md=j; } } if (~mn) print(md,mn); else puts("-1"); return 0; }

3|0B - Two LIS Sum


一眼猜结论题,不难发现我们将A排成1,2,,n时答案一定不会变劣,因此在此基础上求出B的LIS即可

#include<cstdio> #include<iostream> #define RI register int #define CI const int& using namespace std; const int N=300005; int n,a[N],x,b[N],cur,ans; class Tree_Array { private: int bit[N]; public: #define lowbit(x) (x&-x) inline void add(int x,CI y) { for (;x<=n;x+=lowbit(x)) bit[x]=max(bit[x],y); } inline int get(int x,int ret=0) { for (;x;x-=lowbit(x)) ret=max(ret,bit[x]); return ret; } #undef lowbit }T; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]); for (i=1;i<=n;++i) scanf("%d",&x),b[a[i]]=x; for (i=1;i<=n;++i) cur=T.get(b[i]),T.add(b[i],cur+1),ans=max(ans,cur+1); return printf("%d",ans+n),0; }

4|0C - Avoid Prime Sum


又是比较显然的构造题,不难发现奇数+奇数、偶数+偶数的和一定为合数,因此我们考虑尽可能地让奇数之间,偶数之间尽量邻近在一起

因此就有一个naive的想法,根据n的奇偶性把棋盘分成两个部分(如下图),每个部分各自填奇数和偶数,然后只要考虑交界处的情况了

偶数的情况比较容易,只要找到一个奇数且不是质数的数然后拆分一下即可,但奇数时要讨论最中间的奇数要和两个偶数之和为合数

可能我的实现比较渣,代码量挺大,而且还需要特判n=3的情况(话说我n=3的情况手玩玩不出来最后还是暴枚出来的)

后来看了眼官方sol发现可以直接用3的倍数来构造,这样大大降低了代码复杂度

#include<cstdio> #include<iostream> #include<algorithm> #define RI register int #define CI const int& using namespace std; const int N=1005; const int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1}; int n,a[N][N],tar,odd[N],even[N],spo=-1,spe,arr[N]; bool vis[N*N]; inline bool is_prime(CI n) { for (RI i=2;i*i<=n;++i) if (n%i==0) return 0; return 1; } inline bool check(CI t) { for (RI i=1;i<=n;++i) { odd[i]=i; even[i]=t-i; if ((t-i)&1) swap(odd[i],even[i]); if (i!=1&&!is_prime(even[i]+1)) spo=odd[i],spe=even[i]; } return ~spo; } inline void sp_solve(void) { RI i,j,k; for (i=1;i<=9;++i) arr[i]=i; do { for (k=0,i=1;i<=3;++i) for (j=1;j<=3;++j) a[i][j]=arr[++k]; bool flag=1; for (i=1;i<=3&&flag;++i) for (j=1;j<=3&&flag;++j) for (k=0;k<4&&flag;++k) { int ii=i+dx[k],jj=j+dy[k]; if (ii<1||ii>3||jj<1||jj>3) continue; if (is_prime(a[i][j]+a[ii][jj])) flag=0; } if (flag) return; } while (next_permutation(arr+1,arr+10)); } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i,j,k; if (scanf("%d",&n),n==3) sp_solve(); else { if (n&1) { for (i=2*n+1;i<=n*n;i+=2) if (!is_prime(i)&&check(i)) break; a[(n+1)/2][(n+1)/2]=1; a[(n+1)/2+1][(n+1)/2]=even[1]; a[(n+1)/2][(n+1)/2+1]=spe; a[(n+1)/2-1][(n+1)/2+1]=spo; vis[1]=vis[even[1]]=vis[spe]=vis[spo]=1; for (i=j=1;i<(n+1)/2;++i) { if (odd[++j]==spo) ++j; vis[a[(n+1)/2][i]=odd[j]]=1; } for (i=(n+1)/2+2;i<=n;++i) { if (odd[++j]==spo) ++j; vis[a[(n+1)/2-1][i]=odd[j]]=1; } for (i=j=1;i<(n+1)/2;++i) { if (even[++j]==spe) ++j; vis[a[(n+1)/2+1][i]=even[j]]=1; } for (i=(n+1)/2+2;i<=n;++i) { if (even[++j]==spe) ++j; vis[a[(n+1)/2][i]=even[j]]=1; } for (k=1,i=1;i<(n+1)/2-1;++i) for (j=1;j<=n;++j) { while (vis[k]) k+=2; vis[a[i][j]=k]=1; } for (i=1;i<=(n+1)/2;++i) { while (vis[k]) k+=2; vis[a[(n+1)/2-1][i]=k]=1; } for (k=2,i=(n+1)/2+2;i<=n;++i) for (j=1;j<=n;++j) { while (vis[k]) k+=2; vis[a[i][j]=k]=1; } for (i=(n+1)/2+1;i<=n;++i) { while (vis[k]) k+=2; vis[a[(n+1)/2+1][i]=k]=1; } } else { for (i=2*n+1;i<=n*n;i+=2) if (!is_prime(i)) { check(i); break; } for (i=1;i<=n;++i) vis[a[n/2][i]=odd[i]]=1,vis[a[n/2+1][i]=even[i]]=1; for (k=1,i=1;i<n/2;++i) for (j=1;j<=n;++j) { while (vis[k]) k+=2; vis[a[i][j]=k]=1; } for (k=2,i=n/2+2;i<=n;++i) for (j=1;j<=n;++j) { while (vis[k]) k+=2; vis[a[i][j]=k]=1; } } } for (i=1;i<=n;++i) for (j=1;j<=n;++j) printf("%d%c",a[i][j]," \n"[j==n]); return 0; }

5|0D - Simultaneous Sugoroku


当时比赛的时候想的方向有点偏了,结果G了

首先我们直接考虑所有的[1,106]的pieces,然后可以发现一个重要的性质:

对于t,t两个位置的pieces,它们如果到达原点那时间必然时相同的,若无法到达最后的位置关于原点对称

初始时所有的pieces都在一个半轴上,不难发现在每次移动后就可能会出现正负半轴上都有pieces的情况

但我们利用上面的性质,总是可以把其中那个pieces数量较少的半轴上的pieces全部合并到另一个半轴上去,具体地我们可以连一条边在这两个点之间(可以看下图帮助理解)

然后每次有piece到达原点的时候统计答案即可,最后所有点之间构成了一个森林,更新答案即可

#include<cstdio> #include<vector> #define RI register int #define CI const int& #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; const int N=1000005; int n,m,x[N],d[N]; pair <int,int> ans[N]; vector <int> v[N]; bool vis[N]; inline void DFS(CI now) { vis[now]=1; for (int to:v[now]) if (!vis[to]) { if (ans[now].fi==1) ans[to]=ans[now]; else ans[to]=mp(0,-ans[now].se); DFS(to); } } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i,j; for (scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%d",&x[i]); for (i=1;i<=m;++i) scanf("%d",&d[i]); for (i=1;i<=1000000;++i) ans[i].first=-1; int l=1,r=1000000,mid,sum=0; for (i=1;i<=m;++i) { if (l>r) break; if (l+sum>0) sum-=d[i]; else sum+=d[i]; mid=-sum; if (mid<l||mid>r) continue; ans[mid]=mp(1,i); if (mid-l<=r-mid) { for (j=l;j<mid;++j) v[2*mid-j].pb(j); l=mid+1; } else { for (j=mid+1;j<=r;++j) v[2*mid-j].pb(j); r=mid-1; } } for (i=l;i<=r;++i) ans[i]=mp(0,i+sum); for (i=1;i<=1000000;++i) if (!vis[i]&&~ans[i].fi) DFS(i); for (i=1;i<=n;++i) printf("%s %d\n",ans[x[i]].fi?"Yes":"No",ans[x[i]].se); return 0; }

6|0E - Sliding Window Sort


好棒的思维题,感觉好久没做到过这种像做AGC一样沁人心脾的题目了(话说现在的ARC就是以前AGC了吧)

首先考虑把问题转化一下,原来的操作是先给一个区间排序,然后再把窗口右移一格,现在我们把窗口固定下来,然后把数列左移一格,比如样例1:

(4,1,5,6,2,3)(4,5,6,2,3,1)(5,6,2,3,1,4)(5,6,3,1,4,2)(5,6,1,4,2,3)(5,6,4,2,3,1)

现在我们考虑简化问题,把数列分成左边的L=M1个元素和右边的R=N(M1)个元素

当执行了一次或更多次操作后,左边的M1个元素是单调上升的,因此我们可以把这部分元素用一个集合来看待

现在问题转化为这样,有一个L个元素的集合和一个有R个元素的序列,每次操作我们可以把序列中最左侧的元素移到集合里,然后把集合里最小的元素移动到序列的最右侧

比如对于前面的样例有:

{1,4}(5,6,2,3){4,5}(6,2,3,1){5,6}(2,3,1,4){5,6}(3,1,4,2){5,6}(1,4,2,3){5,6}(4,2,3,1)

接下来由于K的范围很大,因此我们考虑缩小它的范围,不难发现当K=R时集合中的元素就是{R+1,R+2,,N},此时序列中的元素对集合不会造成任何影响,因此只需要考虑序列的情况

不难发现我们把多余的操作次数移动直接计算掉即可,而K<R是更加简单了,序列中有一部分元素不可能被用到,直接把它们删掉即可

那么现在问题变成对于集合{R+1,R+2,,N}(若不为这个答案显然就是0)和序列(x1,x2,,xR),我们需要统计出初始的状态{a1,a2,,aL}(b1,b2,,bR)满足以下条件:

  • a1,a2,,aL,b1中存在某个数等于x1
  • a1,a2,,aL,b1,b2中存在某个数等于x2
  • a1,a2,,aL,b1,b2,b3中存在某个数等于x3

但是我们考虑到若xi1>xi,则bi的值只能是xi,因为bi前面的所有数都一定大于等于xi1,因此只能让它来等于xi

这样我们假设剔除了这些确定的bi后序列中待定的数有R个,由于以上的每一个局面都是在L+1个数中选一个等于xi,因此总方案数为(L+1)R

最后由于第一次排序之前集合中的元素可以随意排列,因此答案要再乘上L!

真是一道妙题!

#include<cstdio> #include<vector> #include<algorithm> #define RI register int #define CI const int& using namespace std; const int N=300005,mod=998244353; int n,m,k,L,R; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i; scanf("%d%d%d",&n,&m,&k); vector <int> b(n); for (i=0;i<n;++i) scanf("%d",&b[i]); rotate(b.begin(),b.begin()+(k%n),b.end()); vector <int> left={b.begin(),b.begin()+m-1},right={b.begin()+m-1,b.end()}; L=left.size(); R=right.size(); if (k<R) right={right.end()-k,right.end()},R=k; else if (k>R) rotate(right.begin(),right.end()-(k-R)%R,right.end()),k=R; bool flag=1; for (i=0;i<left.size()-1&&flag;++i) if (left[i]>left[i+1]) flag=0; for (i=0;i<right.size()&&flag;++i) if (right[i]>left[0]) flag=0; if (!flag) return puts("0"),0; int pre=0,ans=1; for (int cur:right) { if (pre>cur) continue; pre=cur; ans=1LL*ans*(L+1)%mod; } for (i=1;i<=L;++i) ans=1LL*ans*i%mod; return printf("%d",ans),0; }

7|0Postscript


最后把这场结了吧,最近开的坑好多但都没时间补,主要是线下上课了时间变得少了很多

F的难度对于现在的我来说还是太吃力了点,先跑路了


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/16754829.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(105)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示