Description
作为一个资深OIer,你被邀请到位于波兰的CDPR总部参观。但没想到你刚一到就遇到了麻烦。昆特牌的数据库发生了故障。原本昆特牌中有 k种卡牌和n 种阵营,为了平衡,每个阵营拥有的卡牌种数都是相等的,并且每个阵营的数据顺序排列。由于故障,卡牌数据被打乱了,每个阵营现在有ai 种卡牌。因为昆特牌即将迎来重大更新,每种牌的所属阵营并不重要,工程师只想尽快让每个阵营拥有相同数量的卡牌。由于数据库的结构原因,你每单位时间只能将一种牌向左边或右边相邻的一个阵营移动。作为OI选手,这自然是难不倒你,但作为一名卡牌游戏爱好者,你想知道最终的卡牌分布有多少种方案。两种方案不同当且仅当存在一种卡牌,它在两种方案中所属阵营不同。对998244353取模
题解
最终每一个位置的答案都是一样的,
那么枚举一个i,
就可以知道,前i个位置,到底是可以向i+1这个位置移多少张卡牌,还是要从后面移多少张卡牌来满足i这个位置。
考虑这个位置是需要被移过来还是移走,
用组合数统计一下答案就好了。
code
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int mo=998244353;
int n,T,k,a[1000003],m,s;
ll ans,jc[1000003],ny[1000003];
char ch;
void read(int&n)
{
for(ch=getchar();ch<'0'|| ch>'9';ch=getchar());
for(n=0;'0'<=ch && ch<='9';ch=getchar())n=(n<<1)+(n<<3)+ch-48;
}
int ksm(ll x,int y)
{
ll s=1;
for(;y;y>>=1,x=x*x%mo)
if(y&1)s=s*x%mo;
return s;
}
ll C(int x,int y)
{
return jc[y]*ny[x]%mo*ny[y-x]%mo;
}
int main()
{
freopen("gwent.in","r",stdin);
freopen("gwent.out","w",stdout);
ny[0]=jc[0]=1;
for(int i=1;i<1000003;i++)jc[i]=jc[i-1]*i%mo;
ny[1000000]=ksm(jc[1000000],mo-2);
for(int i=1000000;i;i--)ny[i-1]=ny[i]*i%mo;
for(read(T);T;T--)
{
read(n);k=0;
for(int i=1;i<=n;i++)read(a[i]),k=k+a[i];
m=k/n;ans=1;s=0;
for(int i=1;i<=n;i++)
{
s=s+a[i]-m;
if(s<0)ans=ans*C(-s,max(m-s,a[i+1]))%mo;
else ans=ans*C(s,m+s)%mo;
}
printf("%lld\n",ans);
}
}