51Nod1679 连通率

153 篇文章 0 订阅
82 篇文章 0 订阅

题目看这里

一个简单的计数问题

我们先考虑O(n^2)的做法

对每个点dfs一次,求出d[i]表示i的深度,那么对答案贡献就是Σ2^(n-d[i])

那么我们考虑用数据结构动态维护这个d[i],显然可以用线段树来维护dfs序做到

复杂度O(n lg n) 效率很高rank8


看了下solution突然才发现别人都不是这么做的!


不管了反正跑得过而且跑的快。。就是代码比较丑陋

#pragma GCC opitmize("O3")
#pragma G++ opitmize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define M 1000000007
#define LL long long
#define mid (l+r>>1)
#define N 100010
using namespace std;
const int inv=500000004;
struct edge{ int v,nt; } G[N<<1];
int n,cnt=0,h[N],w[N],t,d[N],l[N],r[N]; LL s[N<<2],c[N<<2],ans=0;
inline void ad(LL& x,LL y){ x=(x+y)%M; }
inline void mul(LL& x,LL y){ x=x*y%M; }
inline LL pow(LL x,LL k,LL s=1){
    for(;k;x=x*x%M,k>>=1) k&1?s=s*x%M:0;
    return s;
}
inline void build(int l,int r,int x){
    if(l==r){ s[x]=pow(2,n-d[w[l]]-1); c[x]=1; return; }
    build(l,mid,x<<1);
    build(mid+1,r,x<<1|1);
    s[x]=(s[x<<1]+s[x<<1|1])%M; c[x]=1;
}
inline void pd(int x){
    if(c[x]>1){
        mul(c[x<<1],c[x]);
        mul(c[x<<1|1],c[x]);
        mul(s[x<<1],c[x]);
        mul(s[x<<1|1],c[x]);
        c[x]=1;
    }
}
inline void update(int l,int r,int x,int L,int R,LL k){
    if(L<=l && r<=R){ mul(s[x],k); mul(c[x],k); return; }
    pd(x);
    if(L<=mid) update(l,mid,x<<1,L,R,k);
    if(mid<R) update(mid+1,r,x<<1|1,L,R,k);
    s[x]=(s[x<<1]+s[x<<1|1])%M;
}
inline LL query(int l,int r,int x,int L,int R){
    if(L<=l && r<=R) return s[x];
    pd(x); LL ans=0;
    if(L<=mid) ad(ans,query(l,mid,x<<1,L,R));
    if(mid<R) ad(ans,query(mid+1,r,x<<1|1,L,R));
    return ans;
}
inline void dfs(int x){
    w[++t]=x; l[x]=t;
    for(int v,i=h[x];i;i=G[i].nt)
        if(!l[v=G[i].v]){ d[v]=d[x]+1; dfs(v); }
    r[x]=t;
}
inline void cal(int x,int p){
    ad(ans,query(1,n,1,1,n));
    for(int v,i=h[x];i;i=G[i].nt)
        if((v=G[i].v)!=p){
            update(1,n,1,l[v],r[v],4);
            update(1,n,1,1,n,inv);
            cal(v,x); 
            update(1,n,1,l[v],r[v],(LL)inv*inv%M);
            update(1,n,1,1,n,2);
        }
}
int main(){
    scanf("%d",&n);
    for(int x,y,i=1;i<n;++i){
        scanf("%d%d",&x,&y);
        G[++cnt]=(edge){y,h[x]}; h[x]=cnt;
        G[++cnt]=(edge){x,h[y]}; h[y]=cnt;
    }
    dfs(1);
    build(1,n,1);
    cal(1,0);
    ad(ans,-n*pow(2,n-1)%M);
    printf("%lld\n",(ans+M)%M*inv%M);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值