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;
}
posted @ 2020-11-29 22:23  jz_597  阅读(515)  评论(5编辑  收藏  举报