HDU 6036 Division Game

HDU 6036 Division Game

考虑每堆石头最多操作 $ \sum e $ 次,考虑设 $ f(x) $ 表示某一堆石头(最开始都是一样的)操作 $ x $ 次后变成了 $ 1 $ 的方案数量。

明显的,某一堆石头操作了 $ x $ 次后仍然没有变成 $ 1 $ 的方案数量是 $ f(x+1) $。因为最后一次操作必然是把石头从某个数字拿到1。(这个算个小trick吧?)

那么对于第 \(k\) 堆石头答案就是 $ f^{k-1}(x+1) f^{m-i+1}(x) $

因为前 $ k - 1 $ 堆石头进行了 $ x $ 次拿石头的操作还没变成 $ 1 $,而从 $ k $ 后面所有的石头都进行 $ x - 1 $ 次操作并且没变成 $ 1 $ ,而第 $ k $ 堆石头是变成了 $ 1 $ 的。

然后考虑怎么计算 $ f(x) $

相当于我们有 $ m $ 种球每种 $ e_i $ 个放进 $ x $ 个不同的盒子里面,并且最后不能有盒子是空的

如果可以有盒子是空的,这个会比较好算,对每种球分开考虑,并且分别用插板法,最后乘法原理答案是 $ f‘(x)=\displaystyle \prod_{i=1}^m\binom{e_i+x-1}{x-1} $

但是这个还不是答案,因为可以为空,不能为空可以考虑容斥,总方案数减去至少一个为空加上至少两个为空... 容斥系数是 $ (-1)^{t} $ 其中 $ t $ 代表至少有 $ t $ 个位置是空的。

$ f(x) = \displaystyle\sum_{i=0}^x (-1)^{x-i}f‘(i)\binom{x}{i} $

$ f‘(x) $ 化一下发现很容易求所以 $ f(x) $ 就可以NTT辣

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
#define P 985661441
#define MAXN (1 << 19) + 13
int m , k;
int a[MAXN];
int Pow(int x,int y) {
    int res=1;
    while(y) {
        if(y&1) res=res*(ll)x%P;
        x=x*(ll)x%P,y>>=1;
    }
    return res;
}
int wn[2][MAXN];
void getwn(int l) {
    for(int i=1;i<(1<<l);i<<=1) {
        int w0=Pow(3,(P-1)/(i<<1)),w1=Pow(3,P-1-(P-1)/(i<<1));
        wn[0][i]=wn[1][i]=1;
        for(int j=1;j<i;++j)
            wn[0][i+j]=wn[0][i+j-1]*(ll)w0%P,
                    wn[1][i+j]=wn[1][i+j-1]*(ll)w1%P;
    }
}
int rev[MAXN];
void getr(int l) { for(int i=1;i<(1<<l);++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<l-1); }
void NTT(int *A,int len,int f) {
    for(int i=0;i<len;++i) if(rev[i]<i) swap(A[i],A[rev[i]]);
    for(int l=1;l<len;l<<=1)
        for(int i=0;i<len;i+=(l<<1))
            for(int k=0;k<l;++k) {
                int t1=A[i+k],t2=A[i+l+k]*(ll)wn[f][l+k]%P;
                A[i+k]=(t1+t2)%P;
                A[i+l+k]=(t1-t2+P)%P;
            }
    if( f == 1 ) for(int inv=Pow(len,P-2),i=0;i<len;++i) A[i]=A[i]*(ll)inv%P;
}
int J[MAXN] , invJ[MAXN] , p[MAXN];
int kase = 0;
int pp[20] , E[20] , F[MAXN] , n;
signed main() {
    J[0] = invJ[0] = 1;
    for( int i = 1 ; i < MAXN ; ++ i )
        J[i] = 1ll * J[i - 1] * i % P , invJ[i] = Pow( J[i] , P - 2 );
    while( cin >> m >> k ) {
        memset( p , 0 , sizeof p ) , memset( F , 0 , sizeof F );
        n = 1;
        for( int i = 1 ; i <= m ; ++ i ) {
            scanf("%d%d",&pp[i],&E[i]);
            n += E[i];
        }
        p[0] = 1;
        for( int i = 1 ; i <= n ; ++ i ) {
            F[i] = invJ[i] , p[i] = ( ( ( i & 1 ) ? -1 : 1 ) * invJ[i] + P ) % P;
            for( int j = 1 ; j <= m ; ++ j )
                F[i] = 1ll * F[i] * J[E[j] + i - 1] % P * invJ[i - 1] % P * invJ[E[j]] % P;
        }
//      cout << F[2] << endl;
        int len = 1 , l = 0;
        while( len <= n * 2 ) len <<= 1 , ++ l;
        getr( l ) , getwn( l );
        NTT( F , len , 0 ) , NTT( p , len , 0 );
        for( int i = 0 ; i < len ; ++ i ) F[i] = 1ll * F[i] * p[i] % P;
        NTT( F , len , 1 );
        for( int i = 0 ; i < len ; ++ i ) F[i] = 1ll * F[i] * J[i] % P;
//      cout << F[2] << endl;
        printf("Case #%d: ",++kase);
        for( int i = 1 ; i <= k ; ++ i ) {
            int res = 0;
            for( int x = 0 ; x < n ; ++ x )
                res = ( res + 1ll * Pow( F[x + 1] , i - 1 ) * Pow( F[x] , k - i + 1 ) % P ) % P ;
            printf("%d",res);
            if( i != k ) putchar(' ');
        }
        puts("");
    }
}

原文地址:https://www.cnblogs.com/yijan/p/hdu6036.html

时间: 2024-11-11 17:11:14

HDU 6036 Division Game的相关文章

hdu 3480 Division (斜率优化)

Division Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 999999/400000 K (Java/Others) Total Submission(s): 2676    Accepted Submission(s): 1056 Problem Description Little D is really interested in the theorem of sets recently. There's a pro

hdu 2615 Division(暴力)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2615 题解:挺简单的暴力枚举,小小的分治主要是看没人写题解就稍微写一下 #include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; typedef long long ll; ll a[123] , ans , sum[123

HDU 3480 Division(斜率优化+二维DP)

Division Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 999999/400000 K (Java/Others) Total Submission(s): 3984    Accepted Submission(s): 1527 Problem Description Little D is really interested in the theorem of sets recently. There’s a pro

HDU 3480 Division(斜率DP裸题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3480 题目大意:将n个数字分成m段,每段价值为(该段最大值-该段最小值)^2,求最小的总价值. 解题思路:很单纯的斜率优化DP,得出状态转移方程:dp[i][j]=min{dp[k][j-1]+(a[i]-a[k+1])^2}(j-1<=k<i),然后斜率优化降到O(n^2)就好了. 注意:数据类型建议用int,不要用long long,后者乘法计算时间是前者的四倍,否则C++可能会超时. 代码:

HDU 3480 - Division - [斜率DP]

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3480 Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 999999/400000 K (Java/Others) Little D is really interested in the theorem of sets recently. There's a problem that confused him a long time.   

HDU 3480 Division DP + 四边形优化

水题,证明有单调性之后直接照着拍就好 #include <cstdio> #include <cstring> #include <algorithm> #include <climits> using namespace std; #define sq(x) ((x)*(x)) const int maxn = 10005; const int maxm = 5005; int f[maxn][maxm], s[maxn][maxm]; int val[m

HDU 3480 division

题目大意:一个有n个数的集合,现在要求将他分成m+1个子集,对子集i设si表示该集合中最大数与最小数的差的平方.求所有si的和的最小值.n<=10000,m<=5000. 分析:最优解的m个集合肯定不会相交,也不会出现空集,而且每个子集的数必定是连续的. 所以可以将n个数先排序,在来进行dp求解. f[i][j]表示前j个数分成i个集合的最优解. 转移方程为:f[i][j]=min(f[i-1][k]+(num[j]-num[k+1])^2 设决策点k1<k2,若有k2比k1更优,则有:

hdu 3480 Division(四边形不等式优化)

Problem Description Little D is really interested in the theorem of sets recently. There’s a problem that confused him a long time.  Let T be a set of integers. Let the MIN be the minimum integer in T and MAX be the maximum, then the cost of set T if

ACM总结——dp专辑(转)

感谢博主——      http://blog.csdn.net/cc_again?viewmode=list       ----------  Accagain  2014年5月15日 动态规划一直是ACM竞赛中的重点,同时又是难点,因为该算法时间效率高,代码量少,多元性强,主要考察思维能力.建模抽象能力.灵活度. 本人动态规划博客地址:http://blog.csdn.net/cc_again/article/category/1261899 ***********************