ARC109E 1D Reversi Builder
一排格子,每个格子黑白颜色\(c_i\)。一开始从某个位置\(s\)开始,\(s\)上放颜色\(c_i\)的棋子。每次选择一个和有棋子格子相邻的空格子\(x\),放颜色为\(c_x\)的棋子,并找到离\(x\)最近的颜色同为\(c_x\)棋子\(y\),将\(x,y\)之间的棋子都反色。如果找不到就算了。
(其实就是黑白棋)
两人博弈,先手最大化黑子个数。
对于所有的\(s\),分别求:对所有的染色方案\(\{c_i\}\)求最终黑子个数的和(期望)。
\(n\le 2*10^5\)
发现了性质之后它似乎就变成一道简单题了……
虽然我是打表发现的。
可以发现无论怎样操作,中间的连通块不可能出现黑白黑的情况。可以归纳。
根据以上结论,进一步发现,如果两端同色,最终所有棋子一定同色;如果两端异色,先手希望尽量往黑色那边走,后手希望尽量往白色那边走,谁先走到,中间一大块(即:除去两端的同色段)就是谁的。
根据这个性质计算即可。
有个比较阳间的算法:首先算两端同色。对于两端异色,设左端连续段为\([1,L]\),右端连续段为\([R,n]\),当\(s-L\neq R-s\)时,一种状态和它完全取反的状态的答案是互补的。所以只需要特别计算\(s-L=R-s\)的情况多加一次即可。
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 200005
#define ll long long
#define mo 998244353
ll qpow(ll x,ll y=mo-2){
ll r=1;
for (;y;y>>=1,x=x*x%mo)
if (y&1)
r=r*x%mo;
return r;
}
int n;
ll pw2[N*2],f[N];
ll ans[N];
void calc(int n){
pw2[0]=1;
for (int i=1;i<=n*2;++i)
pw2[i]=pw2[i-1]*2%mo;
ll sum=0;
(sum+=pw2[n-2]*n)%=mo;
(sum+=pw2[n-2]*n)%=mo;
for (int i=1;i<=n;++i)
ans[i]=sum;
// for (int i=1;i<=n;++i)
// for (int L=1;L<=n;++L){
// int R=i*2-L;
// if (R>=L+3 && R<=n)
// (ans[i]+=(R-L-1)*pw2[R-L-3])%=mo;
// }
// for (int i=1;i<=n;++i)
// for (int t=2;t<=i-max(2*i-n,1);++t)
// (ans[i]+=(2*t-1)*pw2[2*t-3])%=mo;
for (int i=2;i<=n;++i)
f[i]=(f[i-1]+(2*i-1)*pw2[2*i-3])%mo;
for (int i=3;i<=n-2;++i){
// assert(i-max(2*i-n,1)>=0);
(ans[i]+=f[i-max(2*i-n,1)]-f[2-1]+mo)%=mo;
}
ll inv=qpow(pw2[n]);
for (int i=1;i<=n;++i)
ans[i]=ans[i]*inv%mo;
}
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%d",&n);
if (n==1){
printf("%d\n",qpow(2));
return 0;
}
calc(n);
for (int i=1;i<=n;++i)
printf("%lld\n",ans[i]);
return 0;
}