1. 程式人生 > >jozj5945. 【NOIP2018模擬11.02】昆特牌

jozj5945. 【NOIP2018模擬11.02】昆特牌

5945. 【NOIP2018模擬11.02】昆特牌

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

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

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

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 在這裡插入圖片描述

分析:求均攤紙牌的方案數。 考慮貪心的過程,轉移形成了一個DAG。在圖上DP即可。

程式碼

#include <cstdio>
#define ll long long
#define mo 998244353
#define N 1000005
using namespace std;

ll pow[N],ny[N],a[N],sum[N];
int T,n;

ll ksm(ll x, ll y)
{
	ll base = x, r = 1;
	while (y)
	{
		if (y & 1) r = (r * base) % mo;
		base = (base * base) % mo;
		y /= 2;
	}
	return r;
}

ll C(int n, int m) {return pow[n] * ny[m] % mo * ny[n - m] % mo;}

int main()
{
	freopen("gwent.in","r",stdin);
    freopen("gwent.out","w",stdout);
	scanf("%d", &T);
	pow[0] = ny[0] = 1;
	for (int i = 1; i <= N; i++)
	{
		pow[i] = pow[i - 1] * i % mo;
		ny[i] = ksm(pow[i], mo - 2);
	}
	while (T--)
	{
		scanf("%d", &n);
		ll ans = 1;
		for (int i = 1; i <= n; i++) 
		{
			scanf("%lld", &a[i]);
			sum[i] = sum[i - 1] + a[i];
		}
		ll x = sum[n] / n;
		for (int i = 1; i <= n; i++)
		{
			ll now = sum[i] - x * i;
			if (now >= 0)
			{
				ans = (ans * C(a[i], now)) % mo;
				a[i + 1] += now;
				a[i] -= now;
			}
			else
			{
				ll s = x * (i + 1) - sum[i];
				ans = (ans * C(s, -now)) % mo;
			}
		}
		printf("%lld\n", ans);
	}
}