BZOJ

题目大意

给一张n个点的无向完全图,每条边有一个1~l的全值,问有多少张图满足d[n]=k(d[i]表示1到i的最道路)
数据范围 n,k<=12,l<=10^9

动态规划

枚举1到每个点的最短路d[i]。当我们知道每个点的最短路时,是可以通过计算来得到方案总数的。
一定存在d[1]=0,d[n]=k。
对于d[i]>=k的值,我们不关心它到底是多少,可以直接用k表示。
对于2~n-1的点,我们也不需要每个点的d[i]具体是多少。我们只需要知道有多少点i满足d[i]=s。
另外,对于点i,一定存在一个点j,使得 d[j]<d[i] 且j到i的边长度为d[i]-d[j]。
然后我们可以从小到大枚举d[i]=s的点的个数,记作a[s]。
如何转移呢?
考虑a[s]=1,且d[i]=s时,d[j]=0~s-1的点中至少要有一个满足d[j]+x=d[i][(x为i,j之间边的权值)。这个直接算比较难算,可以用容斥,我们可以算出所有j都满足d[j]+x>=d[i]的方案,再算出所有j都满足d[j]+x>d[i]的方案,相减即为所求,我们设求出的值为p。
当a[s]>1时,我们发现,满足d[i]=s的点之间互不影响,所以方案数应该是 pa[s]
同时,这些点两两之间的边的权值可任意,所以总方案数还要乘上 l(a[s]+1)a[s]2
对于d[i]>=k的情况类似(其实更简单)。
最终答案,我们还要考虑到底哪些点的d[i]=s,于是要用到组合数。
例如,(s>0)a[s]=A,a[s+1]=B,a[s+2]=C,其余a都为0。答案便要乘上
CAn2CBn2ACCn2AB
为什么不用乘上 CCn2AB 因为显然A+B+C=n-2,所以 CCn2AB =1

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=13;const int p=1000000007;
ll ans,w=1,c[maxn][maxn];
int a[maxn],n,k,l;
void dfs(int x,int d,ll s){
    if (x>n){
        d=k;ll z=1,zz=1;
        for (int i=0;i<=d-1;i++) for (int j=1;j<=a[i];j++) {z=z*(l+i-d+1)%p;zz=zz*(l+i-d)%p;}
        z=s*(z-zz+p)%p;for (int j=1;j<=a[d];j++) z=z*l%p;
        int t=0,n1=n;
        for (int i=1;i<=d;i++) if (a[i]>0) {
            z=z*c[n1][a[i]]%p;n1-=a[i];
        }
        ans=(ans+z)%p;
        return;
    }if (d>l) return;
    if (d<k)dfs(x,d+1,s);
    ll z=1,zz=1;if (d==k) zz=0;
    for (int i=0;i<=d-1;i++) for (int j=1;j<=a[i];j++) {z=z*(l+i-d+1)%p;zz=zz*(l+i-d)%p;}z=(z-zz+p)%p;
    for (int j=1;j<=a[d];j++) z=z*l%p;
    a[d]++;
    dfs(x+1,d,s*z%p);
    a[d]--;
}
int main(){
    scanf("%d%d%d",&n,&k,&l);n-=2;
    c[1][0]=c[1][1]=1;
    for (int i=2;i<=12;i++) {c[i][0]=1;for (int j=1;j<=i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%p;}
    for (int i=2;i<=n;i++) w*=i;
    a[0]=1;dfs(1,1,1);
    printf("%lld",ans%p);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值