[bzoj5473]仙人掌

传送门

这个题首先可以考虑一个点也算作一个联通块
去掉所有的边那么答案即为\(2^m*n\)
然后发现每加上一条边就会使联通块的数量减1,这部分是\(-2^{m-1}*m\)
但是这是一个仙人掌图,在环上这样做是错的
简单分析可以发现环上这样做会导致多减,加回来就可以了,这部分是\(2^{m-len}\)(len为环内的边数)
这样我们就算出所有联通块数量的期望了(包括大小为1的联通块)
然后考虑如何去掉大小为1的联通块,考虑到一个点只有周围的边都被去掉才会被孤立,可以得到这部分是\(-\sum_{i=1}^{n}2^{m-deg[i]}\)
代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
void read(int &x) {
    char ch; bool ok;
    for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
    for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=1e6+1,mod=1e9+7;
int dfn[maxn],id,ans,cnt,n,m,pre[maxn*4],nxt[maxn*4],h[maxn],deg[maxn];
int mi(int a,int b)
{
    int ans=1;
    while(b)
    {
        if(b&1)ans=1ll*ans*a%mod;
        b>>=1,a=1ll*a*a%mod;
    }
    return ans;
}
void add(int x,int y)
{
    pre[++cnt]=y,nxt[cnt]=h[x],h[x]=cnt;
    pre[++cnt]=x,nxt[cnt]=h[y],h[y]=cnt;
}
void dfs(int x,int fa)
{
    dfn[x]=dfn[fa]+1;
    for(rg int i=h[x];i;i=nxt[i])
        if(pre[i]!=fa)
        {
            if(!dfn[pre[i]])dfs(pre[i],x);
            else if(dfn[pre[i]]<dfn[x])
            {
                int t=dfn[x]-dfn[pre[i]]+1;
                ans=(ans+mi(2,m-t))%mod;
            }
        }
}
signed main()
{
    read(n),read(m);
    for(rg int i=1,x,y;i<=m;i++)read(x),read(y),add(x,y),deg[x]++,deg[y]++;
    ans=(1ll*mi(2,m)*n%mod-(1ll*mi(2,m-1)*m%mod)+mod)%mod;dfs(1,0);
    for(rg int i=1;i<=n;i++)ans=(ans-mi(2,m-deg[i])+mod)%mod;
    printf("%d\n",ans);
}
posted @ 2019-01-16 12:44  蒟蒻--lichenxi  阅读(312)  评论(2编辑  收藏  举报