• cf111D Petya and Coloring 组合数学,二项式反演


    http://codeforces.com/contest/111/problem/D

    Little Petya loves counting. He wants to count the number of ways to paint a rectangular checkered board of size n × m (n rows, m columns) in k colors. Besides, the coloring should have the following property: for any vertical line that passes along the grid lines and divides the board in two non-empty parts the number of distinct colors in both these parts should be the same. Help Petya to count these colorings.

    Input

    The first line contains space-separated integers n, m and k (1 ≤ n, m ≤ 1000, 1 ≤ k ≤ 106) — the board's vertical and horizontal sizes and the number of colors respectively.

    Output

    Print the answer to the problem. As the answer can be quite a large number, you should print it modulo 109 + 7 (1000000007).

    Examples
    Input
    2 2 1
    Output
    1
    Input
    2 2 2
    Output
    8
    Input
    3 2 2
    Output
    40




    题意:
    给出一个 n*m 的矩阵,用sum种颜色染色
    满足:任意一条竖直线(纵线)把矩阵划分成的2个部分,2个部分的不同的颜色数相同
    求方案数
    n,m <= 10^3,sum <= 10^6

    solution:
    注意到n,m的范围不大

    显然有以下性质:
    1.第1列和第m列的颜色数一定相等
    2.2~m-1列的颜色只能从1,m列的颜色的交集中选择

    m=1的时候,特殊处理,ans=k^n
    m>1的时候,我们只需要考虑1,m列的颜色选择还有交集大小

    预处理g[i]表示恰好用i种颜色涂满n个格子的方案数
    (此时n是一个常量)

    如果用递推式,求g[i]需要O(n^2),求g数组需要O(n^3),tle
    考虑二项式反演求g数组:
    设h(i)表示用i种颜色染n个格子的方案数,则h(i) = i^n
    g(i)表示恰好用i种颜色染n个格子的方案数,
    有:h(y) = sigma(i=0,i<=y)(C(y,i)*g(i))
    则:g(y) = sigma(i=0,i<=y)((-1)^(y-i) * C(y,i) * h(i))
         = sigma(i=0,i<=y)((-1)^(y-i) * C(y,i) * i^n)

    这样求g[i]需要O(nlogn),求g数组需要O(n^2*logn)
    当然也可以优化到O(n^2)求g数组

    主要的预处理部分搞定了,然后就是答案了
    退下公式,得到:
    ans = sigma(j=0,j<=min(n,sum))C(sum,j) * j^(m*n-2*n) *
        (sigma(i=0,i<=min(n-j,(sum-j)/2))(C(sum-j,i)*C(sum-j-i,i)*g(i+j)^2))



     1                                             
     2   //File Name: cf111D.cpp
     3   //Author: long
     4   //Mail: 736726758@qq.com
     5   //Created Time: 2016年05月16日 星期一 01时13分24秒
     6                                    
     7 
     8 #include <stdio.h>
     9 #include <algorithm>
    10 #include <iostream>
    11 #include <string.h>
    12 #include <stdlib.h>
    13 #include <math.h>
    14 
    15 #define LL long long
    16 
    17 using namespace std;
    18 
    19 const int MAXN = 1000 + 3;
    20 const int MAXM = 1000000 + 3;
    21 const int MOD = (int)1e9 + 7;
    22 
    23 LL jie[MAXM];
    24 LL g[MAXN];
    25 
    26 LL qp(LL x,LL y){
    27     LL res = 1;
    28     while(y){
    29         if(y & 1) res = res * x % MOD;
    30         x = x * x % MOD;
    31         y >>= 1;
    32     }
    33     return res;
    34 }
    35 
    36 LL get_c(LL x,LL y){
    37     if(x < y) return 0;
    38     if(x == y || y == 0) return 1;
    39     return jie[x] * qp(jie[y] * jie[x - y] % MOD,MOD - 2) % MOD;
    40 }
    41 
    42 void init(int sum,int n){
    43     jie[0] = 1;
    44     for(int i=1;i<MAXM;i++)
    45         jie[i] = jie[i-1] * i % MOD;
    46     int ma = min(sum,n);
    47     LL now;
    48     for(int i=0;i<=ma;i++){
    49         for(int k=0;k<=i;k++){
    50             now = get_c(i,k) * qp(k,n) % MOD;
    51             if((i - k) % 2)
    52                 g[i] = (g[i] - now + MOD) % MOD;
    53             else
    54                 g[i] = (g[i] + now) % MOD;
    55         }
    56     }
    57 }
    58 
    59 LL solve(int n,int m,int sum){
    60     if(m == 1) return qp(sum,n);
    61     init(sum,n);
    62     LL ans = 0,now,tmp;
    63     int ma = min(sum,n);
    64     for(int j=0,ma2;j<=ma;j++){
    65         now = 0;
    66         ma2 = min(n - j,(sum - j) / 2);
    67         for(int i=0;i<=ma2;i++){
    68             (now += jie[sum-j] * qp(jie[sum-j-2*i]*jie[i]%MOD*jie[i]%MOD,MOD - 2) % MOD
    69                 * g[i+j] % MOD * g[i+j] % MOD) %= MOD;
    70         }
    71         tmp = qp(j,(m - 2) * n) % MOD;
    72         (ans += get_c(sum,j) * tmp % MOD * now % MOD) %= MOD;
    73         //cout << j << " " <<ans << endl;
    74     }
    75     return ans;
    76 }
    77 
    78 int main(){
    79     int n,m,k;
    80     scanf("%d %d %d",&n,&m,&k);
    81     printf("%d
    ",(int)solve(n,m,k));
    82     return 0;
    83 }





  • 相关阅读:
    POJ 2991 Crane(线段树)
    HDU 1496 Equations(哈希表)
    POJ 2785 4 Values whose Sum is 0(哈希表)
    挑战程序设计竞赛 3.2 常用技巧精选(一)
    AOJ 0531:Paint Color(二维离散+imos)
    POJ 2549:Subsets(哈希表)
    POJ 3977:Subset(折半枚举+二分)
    CodeForces 148D Bag of mice
    POJ 2151 Check the difficulty of problems
    HDU 3853 LOOPS
  • 原文地址:https://www.cnblogs.com/-maybe/p/5499202.html
Copyright © 2020-2023  润新知