[2018.07.14 T1] B君的第四题

暂无链接

B君的第四题

【问题描述】

已有的事后必再有,已行的事后必再行。

大家还记得Luogo P3959 [NOIP2017]宝藏 吗?

B君看到很多人用很多弱智的方法水过这个题,感到非常气愤。

这个题目就是把原题改成了一个计数题。

考虑到有些同学没有看过这个题目,我们简述这个题目如下。

输入一个图,我们定义一个有根生成树的权值是

e = { x , y } w e × m a x ( d x , d y )

用文字表述就是,枚举每条边,边权乘以两端较深的点的深度。

点的深度是从根节点,走到该点经过的边数。(而不是边权和,和原题定义一样。)

根节点深度为 0 ,与根节点相邻的点深度为 1 ,以此类推。

对于一棵树,他的有根生成树的个数是 n 乘以无根生成树个数,即对于每个无根生成树确定一个根。

输出所有有根生成树的权值之和,对 1000000007 取模的结果。

【输入格式】

第一行两个整数 n , m 表示点数和边数。

接下来 m 行,每行三个整数 x , y , w 表示一条边的两个端点和边权。

【输出格式】

输出所有有根生成树的权值之和,对 1000000007 取模的结果。

【输入样例】

4 5
1 2 1
1 3 3
1 4 1
2 3 4
3 4 1

【输出样例】

303

题解

先把出题人的话放在前面:

1.png

% % %

看过这篇博客的朋友应该知道,我就是那个用弱智方法水过的人。。。

虽然很绝望,但是我好像会 60 分啊,开个 l o n g   l o n g ,唉,稳了稳了。

结果一条链的情况爆 l o n g   l o n g ,讲道理无根生成树个数最多 111 9 不会爆啊,好不容易可以多拿一点部分分又滚粗了。。。

正解大概是个装压 d p 计数,我们用 s o u [ d ] [ i ] [ j ] 表示到根节点距离为 d ,当前生成树集合中非叶子节点集合为 i ,叶子结点为 j 的生成方案数, a n s [ d ] [ i ] [ j ] 表示对应的所有生成树方案权值之和。

每次枚举下一次扩张的集合 k ,由 [ d ] [ i ] [ j ] 转移到 [ d + 1 ] [ i | j ] [ k ] ,注意 i j k = ,所以总复杂度为 O ( n 4 n )

需要预处理 w a y [ i ] [ j ] 表示从 i 集合扩张到 j 集合的方案数,以及 a d d [ i ] [ j ] 表示扩张增加的权值。

计算 w a y [ i ] [ j ] 只需要将 i j 看做二分图,把 i , j 之间的边在 j 部分的度数乘起来即可;计算 a d d [ i ] [ j ] 则是将 j 集合中每个点连到 i 的边的权值和乘以这些边的所在的扩展方案数。

还有另一个做法老子不会留给大家思考:
4.png

我还是太菜了。

代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=(1<<10)+5,N=11,mod=1e9+7;
int n,m,mx,now,x[M<<1],y[M<<1],w[M<<1],i,j,k,d;
ll way[M][M],add[M][M],dw[M],da[M],sou[N][M][M],ans[N][M][M];
void in()
{
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;++i)
    {
        scanf("%d%d%d",&x[i],&y[i],&w[i]);
        --x[i],--y[i],x[i+m]=y[i],y[i+m]=x[i],w[i+m]=w[i];
    }
}
void ac()
{
    m<<=1;mx=1<<n;
    for(i=0;i<mx;++i)
    {
        for(j=0;j<mx;++j)
        {
            if(i&j)continue;
            memset(dw,0,sizeof(dw));memset(da,0,sizeof(da));
            for(k=1;k<=m;++k)if((i>>x[k]&1)&&(j>>y[k]&1))++dw[y[k]],da[y[k]]+=w[k];
            way[i][j]=1;add[i][j]=0;
            for(k=0;k<n;++k)if(j>>k&1)way[i][j]*=dw[k];
            if(!way[i][j])continue;
            for(k=0;k<n;++k)if(j>>k&1)add[i][j]=(add[i][j]+way[i][j]/dw[k]*da[k]%mod)%mod;
        }
    }
    for(i=0;i<n;++i)sou[0][0][1<<i]=1;
    for(d=0;d<n;++d)for(i=0;i<mx;++i)for(j=0;j<mx;++j)
    {
        if((i&j)||0==sou[d][i][j])continue;
        now=(1<<n)-1-i-j;
        for(k=now;;k=(k-1)&now)
        {
            sou[d+1][i|j][k]=(sou[d+1][i|j][k]+sou[d][i][j]*way[j][k]%mod)%mod;
            ans[d+1][i|j][k]=(ans[d+1][i|j][k]+ans[d][i][j]*way[j][k]+sou[d][i][j]*add[j][k]*(d+1)%mod)%mod;
            if(!k)break;
        }
    }
    printf("%lld",ans[n][mx-1][0]);
}
int main(){in();ac();}

猜你喜欢

转载自blog.csdn.net/shadypi/article/details/81045903
t1