【纪中模拟2018.11.02】【JZOJ5945】昆特牌

题目链接:https://jzoj.net/senior/#main/show/5945

Limits

TL: 1e3ms  ML:512Mb

Description

  原本昆特牌中有$k$种卡牌和$n$种阵营,且每个阵营拥有的卡牌种数都是相等的。由于故障,卡牌数据被打乱了,每个阵营现在有$a_{i}$种卡牌。因为昆特牌即将迎来重大更新,每种牌的所属阵营并不重要,工程师只想尽快让每个阵营拥有相同数量的卡牌。由于数据库的结构原因,你每单位时间只能将一种牌向左边或右边相邻的一个阵营移动。问:最终的卡牌分布有多少种方案。两种方案不同当且仅当存在一种卡牌,它在两种方案中所属阵营不同。对$998244353$取模。

Input  

第一行一个整数$T$,表示数据组数。接下来每组数据,第一行一个整数$n$,第二行$n$个数,第$i$个数为$a_{i}$ ,意义见题目描述

Ouput

$T$行,每行一个数表示答案。

Sample Input 1

3

3

2 1 3

3

1 2 3

3

3 2 1

Sample Ouput 1

3

9

9

Sample Explanation 1

对于该组数据,初始为$\{\{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 Input 2

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

Sample Ouput 2

1120
30
24
270

Data Constraint

保证输入合法,$n\mid{}k,\sum_{i=1}^{n}a_i=k$

数据点 $k\le$ $n\le$ $T\le$
1,2 10 5 10
3,4 1000 100 10
5,6 4000 10 10
7~10 1000000 1000 500

 Solution

  考场上,笔者分析题面,回忆起经典贪心例题均分纸牌,认为此题同样可以线性贪心转移求解。

  读入牌堆数$n$、牌堆状态$a$。设最终各牌堆卡牌数为$ave$,则设差序列$b, \forall{}i\in{}[1,n],b_{i}=a_{i}-ave$

  规定操作方向为下界至上界,每次操作$i$牌堆后清零$b_{i}$。

  考虑$b_{i}>0$时,将$i$牌堆中所有超出$ave$的牌移至$i+1$牌堆,方案数为$\lgroup^{a_{i}}_{b_{i}}\rgroup$。

  若$b_{i}<0$,则将$i+1$牌堆中取出$-b_{i}$张牌,移至$i$牌堆。此时若$i+1$牌堆足够多,方案数为$C_{a_{i+1}}^{-b_{i}}$;若$i+1$牌堆不够多,考虑实际移动牌的过程中,为了一遍转移,一定会先将$i+1$牌堆补成刚刚好足够补充$i$牌堆的状态,再将$i$牌堆补齐,同时$a_{i+1}$变为$ave$,方案数为$C_{ave-b_{i}}^{-b_{i}}$。实现过程中,将计算方案的$C$下标取$max$即可。

  核心思想如上。观察数据范围,$k\le{}4e3$时,可以用杨辉三角预处理组合数,时间复杂度$O(k^2+T*n)$;$k\le{}1e6$时,预处理阶乘及其乘法逆元,时间复杂度$O(k+T*n)$

Code

 

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<ctime>
  6 using namespace std;
  7     typedef long long LL;
  8 bool isnum(char ch){
  9     if('0'<=ch&&ch<='9')
 10         return 1;
 11     return 0;
 12 }
 13     char ch;
 14     bool sign;
 15 void read(int& x){
 16     sign=0;
 17     for(ch=getchar();!isnum(ch);ch=getchar())
 18         if(ch=='-')
 19             sign=1;
 20     x=ch-'0';
 21     for(ch=getchar();isnum(ch);ch=getchar())
 22         x=(x<<3)+(x<<1)+ch-'0';
 23     if(sign)
 24         x=-x;
 25 }
 26 void read(LL& x){
 27     sign=0;
 28     for(ch=getchar();!isnum(ch);ch=getchar())
 29         if(ch=='-')
 30             sign=1;
 31     x=ch-'0';
 32     for(ch=getchar();isnum(ch);ch=getchar())
 33         x=(x<<3)+(x<<1)+ch-'0';
 34     if(sign)
 35         x=-x;
 36 }
 37     char stk[23];
 38     int cnt;
 39 void write(LL x){
 40     if(x==0){
 41         putchar('0');
 42         return ;
 43     }
 44     if(x<0){
 45         putchar('-');
 46         x=-x;
 47     }
 48     cnt=0;
 49     while(x){
 50         stk[++cnt]=x%10+'0';
 51         x/=10;
 52     }
 53     int i;
 54     for(i=cnt;i>=1;i--)
 55         putchar(stk[i]);
 56 }
 57     int T,n;
 58     LL a[1003];
 59     LL b[1003];
 60     const LL mod=998244353;
 61     LL jc[1000003];
 62     LL jcr[1000003];
 63 LL qpow(LL a,LL b){
 64     a%=mod;
 65     LL ans=1;
 66     while(b){
 67         if(b&1)
 68             ans=(ans*a)%mod;
 69         a=(a*a)%mod;
 70         b>>=1;
 71     }
 72     return ans;
 73 }
 74     LL x,y,z;
 75 LL C(LL n,LL m){
 76     x=jc[n];
 77     y=(jcr[m]*jcr[n-m])%mod;
 78     z=(x*y)%mod;
 79     return z;
 80 }
 81     LL ans;
 82 LL max(LL x,LL y){
 83     if(x>y)
 84         return x;
 85     return y;
 86 }
 87 int main(){
 88     freopen("gwent.in","r",stdin);
 89     freopen("gwent.out","w",stdout);
 90     read(T);
 91     int i;
 92     jc[0]=jcr[0]=1;
 93     for(i=1;i<=1000000;i++)
 94         jc[i]=(jc[i-1]*i)%mod;
 95     jcr[1000000]=qpow(jc[1000000],mod-2);
 96     for(i=1000000;i>=2;i--)
 97         jcr[i-1]=(jcr[i]*i)%mod;
 98     LL sum,ave;
 99     while(T--){
100         read(n);
101         for(i=1;i<=n;i++)
102             read(a[i]);
103         sum=0;
104         for(i=1;i<=n;i++)
105             sum+=a[i];
106         ave=sum/n;
107         for(i=1;i<=n;i++)
108             b[i]=a[i]-ave;
109         ans=1;
110         for(i=1;i<n;i++)
111             if(b[i]>0){
112                 ans=(ans*C(a[i],b[i]))%mod;
113                 //leap i sends, ans*=C(a[i],b[i])
114                 a[i+1]+=b[i];
115                 b[i+1]+=b[i];                
116                 a[i]-=b[i];
117                 b[i]=0;//set leap i empty
118             }
119             else if(b[i]<0){
120                 ans=(ans*C(max(ave-b[i],a[i+1]),-b[i]))%mod;
121                 /*
122                 leap i gets.
123                 if(a[i+1]>ave)
124                     straight from i+1, ans*=C(a[i],-b[i])
125                 else 
126                     borrow from farther, ans*=C(ave-b[i],-b[i])
127                 */
128                 a[i+1]+=b[i];
129                 b[i+1]+=b[i];
130                 a[i]-=b[i];
131                 b[i]=0;//set leap i empty
132             }
133         write(ans);
134         putchar('\n');//plural data, dont forget \n
135     }
136     return 0;
137 } 
View Code

 

Conclusion

  笔者能力不够,无法证明该贪心策略(极可能是错误的)。欢迎各位前来拍砖。

posted @ 2018-11-07 15:43  汉谡  阅读(223)  评论(0编辑  收藏  举报