zoukankan      html  css  js  c++  java
  • 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
    ",ans);
        }
        
        return 0;
    }
    
    
    




  • 相关阅读:
    java cocurrent并发包
    阻塞队列只有一个线程在同一时刻对其进行或者读或者写
    在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。
    深入理解生产者消费者
    java并发编程阻塞队列
    高并发
    ClassLoader Java中类加载出现在哪个阶段,编译期和运行期? 类加载和类装载是一样的吗
    JAVA设计模式之工厂模式(简单工厂模式+工厂方法模式)
    Java并发编程-Executor框架(转)
    Java主线程等待所有子线程执行完毕再执行解决办法(转)
  • 原文地址:https://www.cnblogs.com/JYYHH/p/8298635.html
Copyright © 2011-2022 走看看