1. 程式人生 > >JZOJ-senior-5945. 【NOIP2018模擬11.02】昆特牌(gwent)

JZOJ-senior-5945. 【NOIP2018模擬11.02】昆特牌(gwent)

Time Limits: 1000 ms Memory Limits: 524288 KB Detailed Limits

Description

作為一個資深OIer,你被邀請到位於波蘭的CDPR總部參觀。但沒想到你剛一到就遇到了麻煩。昆特牌的資料庫發生了故障。原本昆特牌中有 k種卡牌和n 種陣營,為了平衡,每個陣營擁有的卡牌種數都是相等的,並且每個陣營的資料順序排列。由於故障,卡牌資料被打亂了,每個陣營現在有ai 種卡牌。因為昆特牌即將迎來重大更新,每種牌的所屬陣營並不重要,工程師只想儘快讓每個陣營擁有相同數量的卡牌。由於資料庫的結構原因,你每單位時間只能將一種牌向左邊或右邊相鄰的一個陣營移動。作為OI選手,這自然是難不倒你,但作為一名卡牌遊戲愛好者,你想知道最終的卡牌分佈有多少種方案。兩種方案不同當且僅當存在一種卡牌,它在兩種方案中所屬陣營不同。對998244353取模

Input

第一行一個整數T,表示資料組數。 接下來每組資料,第一行一個整數n ,第二行n個數,第i個數為ai ,意義見題目描述

Output

T行,每行一個數表示答案。

Sample Input

Sample Input1 3 3 2 1 3 3 1 2 3 3 3 2 1

Sample Input2 4 3 8 1 0 4 5 0 1 2 4 0 4 0 0 4 1 1 6 0

Sample Output

Sample Output1 3 9 9

樣例解釋 對於第一組資料,初始為{{1,2}{3}{4,5,6}} 移動結束後為 {{1,2}{3,4}{5,6}},{{1,2}{3,6}{4,5}},{{1,2}{3,5}{4,6}}

Sample Output2 1120 30 24 270

Data Constraint

在這裡插入圖片描述

Solution

考慮貪心的過程,發現形成了個DAG圖,用拓撲序轉移,組合數求答案

Code

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cctype>

#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,a,b) for(int i=a;i>=b;--i)
#define ll long long

using
namespace std; const int N=1010,K=1e6,P=998244353; int T,n,num; int a[N],b[N],d[N],q[N],last[N]; int jc[K+5],ny[K+5]; struct edge{int to,next,v;}e[2*N]; inline void read(int &n) { int x=0,w=0; char ch=0; while(!isdigit(ch)) w|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); n=w?-x:x; } int ksm(int a,int b) { int s=1; for(;b;a=(ll)a*(ll)a%P,b>>=1) if(b&1) s=(ll)s*(ll)a%P; return s; } void pre() { jc[0]=1; fo(i,1,K) jc[i]=(ll)jc[i-1]*(ll)i%P; ny[K]=ksm(jc[K],P-2); fd(i,K-1,0) ny[i]=(ll)ny[i+1]*(ll)(i+1)%P; } int C(int n,int m) { return (ll)jc[n]*(ll)ny[m]%P*(ll)ny[n-m]%P; } void link(int x,int y,int z) { e[++num]=(edge){y,last[x],z},last[x]=num,d[y]++; } int main() { freopen("gwent.in","r",stdin); freopen("gwent.out","w",stdout); pre(); read(T); while(T--) { num=0,memset(last,0,sizeof(last)); read(n); int s=0; fo(i,1,n) read(a[i]),s+=a[i]; int v=s/n; fo(i,1,n) b[i]=a[i]-v; fo(i,1,n) { if(b[i]<0) { link(i+1,i,-b[i]); b[i+1]-=abs(b[i]); b[i]=0; } if(b[i]>0) { link(i,i+1,b[i]); b[i+1]+=b[i]; b[i]=0; } } int l=0,r=0,ans=1; fo(i,1,n) if(!d[i]) q[++r]=i; while(l<r) { int x=q[++l]; for(int w=last[x];w;w=e[w].next) { int y=e[w].to; ans=(ll)ans*(ll)C(a[x],e[w].v)%P; a[x]-=e[w].v,a[y]+=e[w].v,--d[y]; if(!d[y]) q[++r]=y; } } printf("%d\n",ans); } }