contest/232/problem/B Table (计数dp)

      话说咱也不知道自己为啥这么菜,1900分的dp要写半天。。但是这个题的思想很关键,还有就是那个欧拉定理,n^k %p 等同于n^(k%(p-1))%p。

题意是给你一个n*m的矩阵,让你往里面填数,没n*n个格子里面恰好有k个的方案数,n<=100,n*m<=1e18,k<=n*n;感觉好像很像个状压一样,不过n的范围就不行了,所以考虑计数dp,这题的关键在于,如果存在一种合法的方案,则对于m%n的这一系的列中,他们的数量都是一样的。所以相同的这些列计算起来即为次方数,但别忘了次方处理要用欧拉定理%(mod-1)。然后就是dp了,dp[i][j]代表前i行选j个的合法方案数,(j<=i*n).状态转移显然:dp[i][j]=dp[i-1][j-p](前面选j-p个)*c[n][p]^cnt[i](这一列选p个)。注意状态顺序不要写成n*k*k的 因为第三层我们只需枚举到(1-n)所以是n*k*n的,复杂度为n的四次方+n方log(1e9+7)

 

 

Table

time limit per test

4 seconds

memory limit per test

256 megabytes

John Doe has an n × m table. John Doe can paint points in some table cells, not more than one point in one table cell. John Doe wants to use such operations to make each square subtable of size n × n have exactly k points.

John Doe wondered, how many distinct ways to fill the table with points are there, provided that the condition must hold. As this number can be rather large, John Doe asks to find its remainder after dividing by 1000000007 (109 + 7).

You should assume that John always paints a point exactly in the center of some cell. Two ways to fill a table are considered distinct, if there exists a table cell, that has a point in one way and doesn't have it in the other.

Input

A single line contains space-separated integers nmk (1 ≤ n ≤ 100; n ≤ m ≤ 1018; 0 ≤ k ≤ n2) — the number of rows of the table, the number of columns of the table and the number of points each square must contain.

Please, do not use the %lld specifier to read or write 64-bit integers in С++. It is preferred to use the cin, cout streams or the %I64d specifier.

Output

In a single line print a single integer — the remainder from dividing the described number of ways by 1000000007 (109 + 7).

Examples

input

Copy

5 6 1

output

Copy

45

Note

Let's consider the first test case:

 

The gray area belongs to both 5 × 5 squares. So, if it has one point, then there shouldn't be points in any other place. If one of the white areas has a point, then the other one also must have a point. Thus, there are about 20 variants, where the point lies in the gray area and 25 variants, where each of the white areas contains a point. Overall there are 45 variants.


#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define _CRT_SECURE_NO_WARNINGS
typedef  long long ll;
using namespace std;
#define rep(i,j,n) for(ll i=j;i<=n;i++)
#define per(i,j,n) for(ll i=j;i>=n;i--)
typedef unsigned long long ull;
typedef unsigned short us;
const ll INF= 1e18+7;
const ll maxx = 2e5+7;
const ll mod= 1e9+7;
const double eps=1e-8;
inline bool read(ll &num){char in;bool IsN=false;in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,k;

ll l[maxx],r[maxx];
ll dp[120][12000];// i,j 前i行选j个
ll cnt[120]; // num个数 
ll c[120][120]; // 组合数 
ll qpow(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1) {
            ans=ans*a%mod;
        }
        b>>=1;
        a=a*a%mod;
    }
    return ans;
}
int main()
{
    ll t;
    ll ans=0;
    c[0][0]=1;
    c[1][0]=1;
    c[1][1]=1;
    rep(i,2,110)
    {
        rep(j,0,i)
        {
            c[i][j]=(c[i-1][j-1]+c[i-1][j]) %mod;
        }
    }
    //rep(i,0,110) dp[0][i]=1;
    rep(i,0,110) c[i][0]=1;
    cin>>n>>m>>k;
    ll p=m%n;
    ll num=m/n;
    rep(i,1,n)
    {
        cnt[i]=num;
        if(p>=i) cnt[i]++;
        cnt[i]%=(mod-1);//  
    }
    rep(i,0,110) dp[i][0]=1;
    rep(i,1,n)
    {
        ll num=min(n,k);
        for(ll p=0;p<=num;p++)
        {
            //printf("%lld %lld\n",n,p);
            ll x=qpow(c[n][p],cnt[i]);
            rep(j,p,k)
            {
                if(j==0) continue;
                dp[i][j]=( dp[i][j]+ (dp[i-1][j-p]*x) %mod)   %mod;
            }
        }
    }

    printf("%lld\n",(mod+dp[n][k]) %mod );

    return 0;
}




/* 
80 500000000000000000 3200
*/

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值