HDOJ 4903 The only survival

Discription:
There is an old country and the king fell in love with a devil. The devil always ask the king to do some crazy things. Although the king used to be wise and beloved by his people. Now he is just like a boy in love and can’t refuse any request from the devil. Also, this devil is looking like a very cute Loli. 

Something bad actually happen. The devil makes this kingdom's people infected by a disease called lolicon. Lolicon will take away people's life in silence. 

Although z*p is died, his friend, y*wan is not a lolicon. Y*wan is the only one in the country who is immune of lolicon, because he like the adult one so much. 

As this country is going to hell, y*wan want to save this country from lolicon, so he starts his journey. 

You heard about it and want to help y*wan, but y*wan questioned your IQ, and give you a question, so you should solve it to prove your IQ is high enough. 

The problem is about counting. How many undirected graphs satisfied the following constraints? 

1. This graph is a complete graph of size n. 
2. Every edge has integer cost from 1 to L. 
3. The cost of the shortest path from 1 to n is k. 

Can you solve it? 

output the answer modulo 10^9+7 

Input

The first line contains an integer T, denoting the number of the test cases. 

For each test case, the first line contains 3 integers n,k,L

T<=5 n,k<=12,L<=10^9.

Output

For each test case, output the answer in one line.

Sample Input

2
3 3 3
4 4 4

Sample Output

8
668

陈立杰大神出的题,想了想又写了写2h就过去了orz
题目大意就是给出n,k,L ,问有多少n个点的带标号的完全无向图,
满足1-n的最短路长度为k,且每条边的权值都>=1且<=L 。

(要不是上午ZHW点拨了几下我估计一晚上都做不出来hhh)

首先不能枚举最短路的构成,这样显然会挂掉,因为1-n可能会有很多条最短的路径;

然后考虑一下枚举每一个dis[i],代表1号节点到i号节点的最短路长度。
假设我们不用管枚举dis的时间花费,先关注一下如果dis都确定了的话如何更新答案。
先把dis从小到大排序,之后从后面的点向前面的每个节点连边(这样才能是完全图嘛。。)。
边权显然不是任意的,可以发现的是如果1到i(代表最短路长度从小到大排序后第i小点)
的最短路长度为dis[i]的话,必须满足:
1.对于所有的dis[j]<dis[i],
使得dis[j]+val(j,i)>=dis[i]
2.存在dis[j]<dis[i],
使得dis[j]+val(j,i)==dis[i]
满足上两个条件的方案数不难求,一个容斥就ojbk了。
然后再考虑dis[j]==dis[i]的点j,发现边权是多少都无所谓,所以都×L就行了。

但是可能会有点的dis>k,这可怎么办呢?
这样就设dis[i]==k(这里i!=n)的意思为1到i的最短路长度>=k,
那么我们统计这类节点的方案数就不用容斥减去了,直接乘上满足条件1的方案数就行了。

但是搜索出每个dis[i]的值是会TLE的,需要考虑一种更加高效的方法。

其实我们不必知道每个dis是多少,只需要知道对于1<=x<=k的每个x,dis[i]==x的i有多少个就行了。
这样我们改变搜索对象(实验证明这样搜索能到达的状态最多有10^5种,再乘上n^2的更新答案时间根本不虚),
中间计算的时候乘上组合数就行了(相当于算等于x的dis[i]分别对应原图中的哪些节点)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<map>
#include<cstring>
#define ll long long
#define ha 1000000007
using namespace std;
ll n,k,L,T,ans;
ll C[105][105],jc[105];
ll num[105],cnt=0;
ll ci[105][105];

inline void init(){
    C[0][0]=jc[0]=1;
    for(int i=1;i<=30;i++){
        C[i][0]=1,jc[i]=jc[i-1]*(ll)i%ha;
        for(int j=1;j<=i;j++){
            C[i][j]=C[i-1][j-1]+C[i-1][j];
            if(C[i][j]>=ha) C[i][j]-=ha;
        }
    }
}

inline ll calc(){
    ll an=1,rk1,rk2,fin=num[k];
    num[k]=1;
    //先算num<k和num==k中的n的情况。
    for(int i=1;i<=k;i++) if(num[i]){
        rk1=rk2=1;
        for(int j=0;j<i;j++) if(num[j]){
            rk1=rk1*ci[i-j-1][num[j]];
            rk2=rk2*ci[i-j][num[j]];
            if(rk1>=ha) rk1-=rk1/ha*ha;
            if(rk2>=ha) rk2-=rk2/ha*ha;
        }
        
        rk1+=ha-rk2;
        if(rk1>=ha) rk1-=ha;
        
        for(int j=1;j<=num[i];j++){
            an=an*rk1;
            rk1=rk1*L;
            if(an>=ha) an-=an/ha*ha;
            if(rk1>=ha) rk1-=rk1/ha*ha;
        }
    }
    
    //再求num==k的其他点的方案数
    num[k]=fin-1;
    if(num[k]){
        rk1=1;
        for(int i=0;i<k;i++) if(num[i]){
            rk1=rk1*ci[k-i-1][num[i]];
            if(rk1>=ha) rk1-=rk1/ha*ha;            
        }
        
        for(int j=1;j<=num[k];j++){
            rk1=rk1*L;
            if(rk1>=ha) rk1-=rk1/ha*ha;
            an=an*rk1;
            if(an>=ha) an-=an/ha*ha;
        }
    }
    
    return an;
}

void dfs(int tmp,int lef,ll alr){
    if(tmp==k-1){
        num[k]=lef+1;
        ans+=alr*calc()%ha;
        if(ans>=ha) ans-=ha;
        return;
    }
    
    for(int u=0;u<=lef;u++){
        ll to=alr*C[lef][u]%ha;
        num[tmp+1]=u;
        dfs(tmp+1,lef-u,to);
    }
}

inline void solve(){
    dfs(0,n,1);
}

int main(){
    init();
    scanf("%lld",&T);
    while(T--){
        ans=0; memset(num,0,sizeof(num));
        memset(ci,0,sizeof(ci));
        
        scanf("%lld%lld%lld",&n,&k,&L);
        if(L<k){
            puts("0");
            continue;
        }
        
        for(int i=0;i<=k;i++){
            ci[i][0]=1;
            for(int j=1;j<=n;j++) ci[i][j]=ci[i][j-1]*(L-i+ha)%ha;
        }
                
        n-=2,num[0]=num[k]=1,solve();
        printf("%lld\n",ans);
    }
    
    return 0;
}

 





posted @ 2018-01-16 20:27  蒟蒻JHY  阅读(237)  评论(0编辑  收藏  举报