[atARC109E]1D Reversi Builder

归纳每一次操作后必然是两个颜色相同的连续段(即ww...bb...或bb...ww...),对操作的位置分类讨论不难证明正确性

当$c_{1}=c_{n}$,由于端点颜色不会修改,再根据该结论,可以得到$f(s,c_{i})=c_{1}cdot n$(w为0,b为$n$)

当$c_{1} e c_{n}$(以下假设$c_{1}=b$且$c_{n}=w$),令$x=min_{c_{i}=w}i$且$y=max_{c_{i}=b}i$,考虑答案的上下限,最坏情况下即为$[1,x)$,最好情况下为$[1,y]$

对$x-1$和$y+1$哪个先取分类讨论:

1.若$x-1$先取,则当选择$y$后(也有可能是先选$y$再选$x-1$,但同理),$[1,y]$必然都为黑色(且不会再被翻转),即达到上限,因此如果$x-1$到$s$的距离小于等于$s$到$y+1$的距离,则答案为$[1,y]$

2.若$y+1$先取,类似的可以得到$[x,n]$都为白色,即答案取到下限$[1,x)$,因此如果$x-1$到$s$的距离大于$s$到$y+1$的距离,则答案为$[1,x)$

(另外对于$c_{1}=w$且$c_{n}=b$,不是两倍而是$s'=n-s+1$时的答案)

这显然包含了所有情况,即答案仅与$x$、$y$和$s$有关,得到了一个暴力$o(n^{3})$的做法,代码如下

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 200005
 4 #define mod 998244353
 5 int n,mi[N],ans[N];
 6 int main(){
 7     scanf("%d",&n);
 8     if (n==1){
 9         printf("%d",(mod+1)/2);
10         return 0;
11     }
12     mi[0]=1;
13     for(int i=1;i<=n;i++)mi[i]=mi[i-1]*2%mod;
14     for(int i=2;i<=n;i++){
15         for(int j=1;j<=n;j++)ans[j]=(ans[j]+i-1)%mod;
16         for(int j=i+1;j<n;j++)
17             for(int k=1;k<=n;k++)
18                 if (abs(i-1-k)<=abs(j+1-k))ans[k]=(ans[k]+1LL*j*mi[j-i-1])%mod;
19                 else ans[k]=(ans[k]+1LL*(i-1)*mi[j-i-1])%mod;
20     }
21     int inv=1;
22     for(int i=1;i<=n;i++)inv=1LL*(mod+1)/2*inv%mod;
23     for(int i=1;i<=n;i++)printf("%lld
",(ans[i]+ans[n-i+1]+1LL*mi[n-2]*n)%mod*inv%mod);
24 }
View Code

将$y=x-1$的特殊情况累加后即为$frac{n(n-1)}{2}$,然后对于$k$的枚举改为差分,时间复杂度降为$o(n^{2})$,代码如下

 1     for(int i=2;i<=n;i++)
 2         for(int j=i+1;j<n;j++){
 3             int k=(j+i)/2;
 4             ans[1]=(ans[1]+1LL*j*mi[j-i-1])%mod;
 5             ans[k+1]=(ans[k+1]-1LL*(j-i+1)*mi[j-i-1]%mod+mod)%mod;
 6         }
 7     for(int i=2;i<=n;i++)ans[i]=(ans[i]+ans[i-1])%mod;
 8     int inv=1,s=1LL*n*(n-1)/2%mod;
 9     for(int i=1;i<=n;i++){
10         inv=1LL*(mod+1)/2*inv%mod;
11         ans[i]=(ans[i]+s)%mod;
12     }
View Code

进一步的,对于$ans[1]$的修改比较好处理,对于$ans[k+1]$的修改可以枚举$j-i$,那么$k=frac{j-i}{2}+i$,再进行一次差分即可,时间复杂度即降为$o(n)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 200005
 4 #define mod 998244353
 5 int n,mi[N],ans[N];
 6 int main(){
 7     scanf("%d",&n);
 8     if (n==1){
 9         printf("%d",(mod+1)/2);
10         return 0;
11     }
12     mi[0]=1;
13     for(int i=1;i<=n;i++)mi[i]=mi[i-1]*2%mod;
14     for(int i=1;i<=n-3;i++){
15         ans[i/2+3]=(ans[i/2+3]-1LL*(i+1)*mi[i-1]%mod+mod)%mod;
16         ans[i/2+n-i+1]=(ans[i/2+n-i+1]+1LL*(i+1)*mi[i-1]%mod)%mod;
17     }
18     for(int i=2;i<=n;i++)ans[i]=(ans[i]+ans[i-1])%mod;
19     for(int i=3;i<n;i++)ans[1]=(ans[1]+1LL*i*(mi[i-2]-1))%mod;
20     for(int i=2;i<=n;i++)ans[i]=(ans[i]+ans[i-1])%mod;
21     int inv=1,s=1LL*n*(n-1)/2%mod;
22     for(int i=1;i<=n;i++){
23         inv=1LL*(mod+1)/2*inv%mod;
24         ans[i]=(ans[i]+s)%mod;
25     }
26     for(int i=1;i<=n;i++)printf("%lld
",(ans[i]+ans[n-i+1]+1LL*mi[n-2]*n)%mod*inv%mod);
27 }
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/14066601.html