列表

详情


NC15255. 白兔的游戏

描述

Nim游戏是这样的:有 n 个石子堆,第 i 个石子堆有 a[i] 个石子,两个人轮流选择一个石子堆并从中拿走一个或者多个石子。拿走最后一个石子的人获胜。

现在有两个人他们决定每次随机选择一个合法决策来操作。现在他们想知道在这种决策方式下先手的胜率以及所有可能的情况中先手获胜的次数,对 998244353 取模。
即设答案化为最简分式后的形式为 a/b ,其中 a 和 b 的互质。 输出整数 x 使得 bx ≡ a mod 998244353 且 0 ≤ x < 998244353。 可以证明这样的整数 x 是唯一的。

输入描述

第一行一个正整数n,表示石子堆数。
第二行一共n个正整数a[i],表示每堆的石子数。

输出描述

一行两个整数,分别表示先手的胜率和所有可能的情况中先手获胜的次数,并对998244353取模。

示例1

输入:

2
1 2

输出:

499122177 3

示例2

输入:

3
1 2 3

输出:

499122177 86

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++11(clang++ 3.9) 解法, 执行用时: 278ms, 内存消耗: 7644K, 提交时间: 2018-03-09 19:25:50

#include <bits/stdc++.h>
using namespace std;

typedef long long  LL;
typedef pair <int,int>  PII;
const int N=50010,mo=998244353,g=3;
int n,s,a[N],fac[N],ifac[N],A[1<<17],B[1<<17],ans;
vector <int> V[N];
priority_queue <PII,vector <PII> ,greater <PII> > Q;

int qpow(int a,int b)
{
	int x=a;  a=1;
	while (b)
		{
			if (b&1)  a=1LL*a*x%mo;
			x=1LL*x*x%mo,b>>=1;
		}
	return a;
}

int C(int n,int m){return 1LL*fac[n]*ifac[m]%mo*ifac[n-m]%mo;}

void NTT(int *a,int n,int op)
{
	for (int i=1,j,x,t; i<n; i++)
		{
			for (x=i,j=0,t=1; t<n; j<<=1,j|=x&1,x>>=1,t<<=1);
			if (i<j)  swap(a[i],a[j]);
		}
	unsigned int w,W,u,v;
	for (int len=2; len<=n; len<<=1)
		{
			w=qpow(g,(mo-1)/len);
			if (op<0)  w=qpow(w,mo-2);
			for (int i=0; i<n; i+=len)
				{
					W=1;
					for (int j=0; j<(len>>1); j++)
						{
							u=a[i+j],v=1LL*a[i+j+(len>>1)]*W%mo,W=1LL*W*w%mo;
							a[i+j]=(u+v>=mo)?(u+v-mo):(u+v);
							a[i+j+(len>>1)]=(u>=v)?(u-v):(u-v+mo);
						}
				}
		}
	if (op<0)
		{
			int inv=qpow(n,mo-2);
			for (int i=0; i<n; i++)  a[i]=1LL*a[i]*inv%mo;
		}
}

void mul(int x,int y)
{
	int N=1,xs=a[x],ys=a[y];
	while (N<=xs+ys)  N<<=1;
	for (int i=xs; i; i--)  A[i]=V[x][i];
	for (int i=ys; i; i--)  B[i]=V[y][i];
	NTT(A,N,1),NTT(B,N,1);
	for (int i=0; i<N; i++)  A[i]=1LL*A[i]*B[i]%mo;
	NTT(A,N,-1);
	a[x]+=a[y],V[x].resize(a[x]+1);
	for (int i=a[x]; i>=0; i--)  V[x][i]=A[i];
	for (int i=0; i<N; i++)  A[i]=B[i]=0;
}

void work()
{
	scanf("%d",&n),s=ans=0;
	for (int i=1; i<=n; i++)  scanf("%d",&a[i]),s+=a[i];
	fac[0]=1;
	for (int i=1; i<=s; i++)  fac[i]=1LL*fac[i-1]*i%mo;
	ifac[s]=qpow(fac[s],mo-2);
	for (int i=s; i; i--)  ifac[i-1]=1LL*ifac[i]*i%mo;
	for (int i=1; i<=n; i++)
		{
			V[i].resize(a[i]+1),Q.push(make_pair(a[i],i));
			for (int j=1; j<=a[i]; j++)  V[i][j]=1LL*C(a[i]-1,j-1)*ifac[j]%mo;
		}
	for (int _=1,x,y; _<n; _++)
		{
			y=Q.top().second,Q.pop(),x=Q.top().second,Q.pop();
			mul(x,y),Q.push(make_pair(a[x],x));
		}
	for (int i=1,x=Q.top().second; i<=s; i+=2)  ans=(ans+1LL*V[x][i]*fac[i])%mo;
	Q.pop();
	printf("%d %d\n",(n==s?(n&1?1:0):499122177),ans);
}

int main()
{    work();
	return 0;
}

上一题