【比赛】NOIP2020

总结

还算正常发挥吧。T1高精度没有管它,T2打的 O ( 26 n + n l o g n ) O(26n+nlogn) O(26n+nlogn),离正解 O ( n ? ) O(n?) O(n?)很近了,只是不知道怎么去把 C C C为奇的个数和 A B AB AB拆分的方案数算出来,就只能用前缀和预处理了。T4很吃亏,本来线段树+暴力有40pts,结果没加取模,估计只有30pts。T3完全不可做,n=2时10pts没打对,又白丢了10pts。

T3,T4总共白丢了20pts,实在不应该。最后大概有210左右吧。对于T3这样的构造题实在无从下手,应该多肝CF

简要题解

本人已经被NOIP搞崩溃了。但本着心平气和的原则,还是把它写完。

T1

略。

T2

扩展 KMP 什么的我都不会,还有什么分析性质分别计数的都看不懂,我就说最简单的做法吧:

先枚举(AB)。然后向后扩展,看有几个AB。答案就是 k ∗ f ( i , p r e [ k ∗ i + 1 ] ) k*f(i,pre[k*i+1]) kf(i,pre[ki+1])

正解
继续分析发现,对于固定的 ( A B ) (AB) (AB),根据重复次数的奇偶性,其对应的 C C C 中出现奇数次的字符个数只有两种可能。

直接统计每种可能对答案的贡献即可。复杂度 O ( n log ⁡ 26 ) O(n \log 26) O(nlog26),期望得分 100 p t s 100pts 100pts

具体做法:
首先我们暴力枚举 X X X 的长度 X = A + B X=A+B X=A+B
然后对于每个长度,我们二分它 i i i 能取到的最大值。
因为长度越长这个字符串越不可能循环,所以具有单调性。
上面的东西是 ∑ i = 1 N log ⁡ ( N i ) \sum_{i=1}^{N}\log\left(\frac{N}{i}\right) i=1Nlog(iN)
据神仙 h ehezhou h e h e z h o u \color{black}\text{h}\color{red}\text{ehezhou}hehezhou hehezhouhehezhou 说是 O ( N ) O(N) O(N) 的。
我们获得了每个 X X X 所能取到的最大 i i i 的长度。
同时,当 X X X 固定时,当 i ∈ { 1 , 3 , 5... } i\in\{1,3,5...\} i{1,3,5...} i ∈ { 2 , 4 , 6... } i\in\{2,4,6...\} i{2,4,6...} 时,把 X X X 拆成 A A A B B B 的方案数是一样的,所以我们可以 O ( 1 ) O(1) O(1) 统计贡献。
然后枚举 X X X,在 X X X 改变时只需要计算增加的那个 X X X 的贡献就好了。
复杂度均摊 O ( N l o g 26 ) O(Nlog26) O(Nlog26)

考场做法

考场上开了一个 s u m [ 2 e 6 ] [ 30 ] sum[2e6][30] sum[2e6][30]的数组,差点以为暴空间了。

然而:2000005 × 30 × 4 ÷ 1024 ÷ 1024 =‬228.8824081420898 MiB

于是乎苟了84pts。

#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cmath> using namespace std; const int N=2e6+5; inline int read() { int x=0,f=1; char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();} return x*f; } char s[N]; int nxt[N],pre[N],pre2[N],bit[27],sum[27],n; bool tag[27]; void Kmp() { nxt[1]=0; for(int i=2,j=0;i<=n;i++) { while(j!=0&&s[i]!=s[j+1]) j=nxt[j]; if(s[i]==s[j+1]) j++; nxt[i]=j; } } int main() { // freopen("string.in","r",stdin); // freopen("string.out","w",stdout); int T=read(); while(T--) { long long ans=0; memset(bit,0,sizeof(bit)); memset(tag,0,sizeof(tag)); scanf("%s",s+1); n=strlen(s+1); Kmp(); pre[n+1]=pre[0]=0; for(int i=n;i>=1;i--) { tag[s[i]-'a']^=1; if(tag[s[i]-'a']) pre[i]=pre[i+1]+1; else pre[i]=pre[i+1]-1; } memset(tag, 0, sizeof(tag)); int cnt=0; for(int i=1;i<n;i++) { int L=0,R=n/(i*2),res=0; while(L<=R) { int mid=(L+R)>>1,len=i*(mid*2+1); if(len<n&&i%(len-nxt[len])==0) L=mid+1,res=mid; else R=mid-1; } ans+=bit[pre[i+1]]*(res+1); L=1,R=n/(i*2),res=0; while(L<=R) { int mid=(L+R)>>1,len=i*(mid*2); if(len<n&&i%(len-nxt[len])==0) L=mid+1,res=mid; else R=mid-1; } if(res>0) ans+=bit[pre[i*2+1]]*res; tag[s[i]-'a']^=1; if(tag[s[i]-'a']) cnt++; else cnt--; for(int j=cnt;j<=26;j++) bit[j]++; } printf("%lld\n",ans); } return 0; }

T3

我们先构造一个全为0的柱子,再把每个柱子的1提上来,把这些1全部放到一个柱子上,这样柱子数就从n变成了n-1。直到只
剩两个颜色,此时上述方法找不到一个没有动过的柱子2。换种方法即可。

操作次数分析
最外层枚举颜色一个 n n n ,每次构造全 1 1 1 列时需要 n m + m nm+m nm+m(因为 1 1 1 的总个数为 m m m,所以分解时的第一步均摊的总次数为 m m m

因为列数随着颜色一个一个处理完会减小,所以 n m + m nm+m nm+m 中的 n n n 其实是个等差数列,也就是 ∑ i = 1 n i m + m \sum\limits_{i=1}^n im+m i=1nim+m。所以有个 1 / 2 1/2 1/2 的常数

构造全 0 0 0 列时上限需要 4 m 4m 4m 次,所以操作次数上限为 ∑ i = 1 n i m + 5 m \sum\limits_{i=1}^n im+5m i=1nim+5m ,极限数据满打满算要操作 600,000 次,可以轻松通过本题

复杂度 == 操作次数,所以不需要管它

另一种思路:分治?
先考虑一种类似于2(3)根柱子的情况:

  • 有两根每根 m m m个珠子的柱子和一根空柱子。 \ \ 废话
  • 有两种颜色col1,col2。(假设col1的个数 ≥ \ge col2的个数)
  • 要求将①柱子装满col1的珠子,③柱仍为空。

怎么最快分离颜色呢?

好了我们可以在两种颜色的情况下快速memset一个柱子。

那怎么变成两种颜色呢???

考虑分治:

若当前颜色集合为 S S S

那么把它分成两份,一份是当 c o l 1 col1 col1,一份当 c o l 2 col2 col2

可是有很多个柱子,那可以两两来做每次可以memset一个柱子。

因为 m ∣ c o u n t o f ( c o l 1 ) m|countof(col1) mcountof(col1) m ∣ c o u n t o f ( c o l 2 ) m|countof(col2) mcountof(col2) 所以最后肯定能使若干个柱子全为 c o l 1 col1 col1,若干个柱子全为 c o l 2 col2 col2

每次大约走 5 n m 5nm 5nm步。

在递归求解被划为 c l o 1 clo1 clo1的color和被划为 c l o 2 clo2 clo2的color。

最后次数 ≤ 5 n m l o g 2 n \le 5nmlog_2n 5nmlog2n

//666 #include<bits/stdc++.h> using namespace std; const int N=55; const int M=405; const int K=820005; struct Node{ int tot,pos,a[M]; int cnt[N]; void Push(int x) { cnt[x]++; a[++tot]=x; } int Top() { return a[tot]; } void Pop() { cnt[a[tot]]--; tot--; } }s[N]; struct Data { int s,t; }P[K]; int n,m,k; bool sol[N]; void Move(int x,int y) { P[++k]=(Data){s[x].pos,s[y].pos}; s[y].Push(s[x].Top()); s[x].Pop(); } void Work(int x,int y,int z,int col) { int t=s[x].cnt[col]; for(int i=1;i<=t;i++) { Move(y,z); } for(int i=1;i<=m;i++) { if(s[x].Top()==col) Move(x,y); else Move(x,z); // if(s[x].cnt[col]==0) break; } for(int i=1;i<=m-t;i++) Move(z,x); for(int i=1;i<=m;i++) { if(s[2].Top()==col||s[1].tot==m) Move(2,z); else Move(2,1); } swap(s[2],s[z]); swap(s[1],s[z-1]); // for(int i=1;i<=t;i++) Move(y,x); // for(int i=1;i<=t;i++) Move(z,y); } void Work2(int x,int y,int z,int col) { int t=s[x].cnt[col]; for(int i=1;i<=t;i++) { Move(y,z); } for(int i=1;i<=m;i++) { if(s[x].Top()==col) Move(x,y); else Move(x,z); } swap(s[x],s[y]); swap(s[y],s[z]); // for(int i=1;i<=t;i++) Move(y,x); // for(int i=1;i<=t;i++) Move(z,y); } void Work3(int x,int y,int z,int col) { int t=s[x].cnt[col]; for(int i=1;i<=t;i++) { Move(y,z); } for(int i=1;i<=m;i++) { if(s[x].Top()==col) Move(x,y); else Move(x,z); } for(int i=1;i<=m-t;i++) Move(z,x); for(int i=1;i<=t;i++) Move(y,x); for(int i=1;i<=t;i++) Move(z,y); } int main() { // freopen("ball3.in","r",stdin); // freopen("ans.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { int x; scanf("%d",&x); s[i].Push(x); } for(int i=1;i<=n+1;i++) s[i].pos=i; for(int i=n;i>2;i--) { Work(1,i,i+1,i); for(int j=1;j<i;j++) { Work2(j,i,i+1,i); } for(int j=1;j<i;j++) { while(s[j].Top()==i) { Move(j,i+1); } } for(int j=1;j<i;j++) { while(s[j].tot!=m) { Move(i,j); } } } Work3(1,2,3,2); Work3(2,1,3,2); while(s[1].Top()==2) Move(1,3); while(s[2].Top()==2) Move(2,3); while(s[2].tot>0) Move(2,1); printf("%d\n",k); for(int i=1;i<=k;i++) printf("%d %d\n",P[i].s,P[i].t); }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530380.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(45)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示