ARC109E 1D Reversi Builder

一排格子,每个格子黑白颜色(c_i)。一开始从某个位置(s)开始,(s)上放颜色(c_i)的棋子。每次选择一个和有棋子格子相邻的空格子(x),放颜色为(c_x)的棋子,并找到离(x)最近的颜色同为(c_x)棋子(y),将(x,y)之间的棋子都反色。如果找不到就算了。

(其实就是黑白棋)

两人博弈,先手最大化黑子个数。

对于所有的(s),分别求:对所有的染色方案({c_i})求最终黑子个数的和(期望)。

(nle 2*10^5)


发现了性质之后它似乎就变成一道简单题了……

虽然我是打表发现的。

可以发现无论怎样操作,中间的连通块不可能出现黑白黑的情况。可以归纳。

根据以上结论,进一步发现,如果两端同色,最终所有棋子一定同色;如果两端异色,先手希望尽量往黑色那边走,后手希望尽量往白色那边走,谁先走到,中间一大块(即:除去两端的同色段)就是谁的。

根据这个性质计算即可。

有个比较阳间的算法:首先算两端同色。对于两端异色,设左端连续段为([1,L]),右端连续段为([R,n]),当(s-L eq 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
",qpow(2));
		return 0;
	}
	calc(n);
	for (int i=1;i<=n;++i)
		printf("%lld
",ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/jz-597/p/14058486.html