[JZOJ5365]通信

来源:互联网 发布:筑业标书制作软件 编辑:程序博客网 时间:2024/06/13 08:59

题目大意

给定一棵n个节点的树。一个方案会随机选择一段连续序号的点,方案的代价为从被选择的点中选择任意一个点,从这个点出发遍历所有的被选择的点,并回到出发点的总路程长度。
求出方案代价的期望值。


题目分析

选出一段点的代价是它们构成的虚树的边的数量的两倍。
看到路径长度期望直接期望线性性拆成边讨论。
如果我们能够统计每一条边两边的连续段方案数那就能求出跨越该边的方案数。考虑使用set维护一个点子树内的所有连续段的开头位置。遍历完一个子树之后采用启发式合并来更新,在这同时更新一下答案就好了。
加入段还要合并再更新答案看起来需要很复杂的分类讨论,其实不然。我们令[x,y]表示插入的块,[l,r]表示插入的块在和两边合并之后形成的块,[st,en]表示插入块向左直到遇到第一个块之前的点,以及向右直到遇到第一个块之前的点,令f(x)=(x+1)x2那么子树内连续段就要加上f(rl+1)减去f(xl)+f(ry),子树外连续段就要加上f(xst)+f(eny)减去f(enst+1)
时间复杂度O(nlog2n)


代码实现

#include <algorithm>#include <iostream>#include <cstdio>#include <cctype>#include <set>using namespace std;typedef pair<int,int> data;typedef set<data>::iterator itr;#define mkp(a,b) make_pair(a,b)#define ft first#define sd secondinline int read(){    int x=0,f=1;    char ch=getchar();    while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();    return x*f;}const int P=1000000007;const int N=100005;const int E=N<<1;int quick_power(int x,int y){    int ret=1;    for (;y;y>>=1,x=1ll*x*x%P) if (y&1) ret=1ll*ret*x%P;    return ret;}int last[N],id[N],ans[N],cnt[N][2];int tov[E],nxt[E];set<data> blocks[N];int n,tot,sum,inv,all;inline int calc(int len){return (1ll*(len+1)*len>>1)%P;}inline void insert(int x,int y){tov[++tot]=y,nxt[tot]=last[x],last[x]=tot;}void dfs(int x,int fa){    id[x]=x,blocks[x].insert(mkp(x,x)),cnt[x][1]=(calc(x-1)+calc(n-x))%P,cnt[x][0]=1;    for (int i=last[x],y;i;i=nxt[i])        if ((y=tov[i])!=fa)        {            dfs(y,x),(sum+=(((all-ans[y]+P)%P)<<1)%P)%=P;            int u=id[x],v=id[y];            if ((int)blocks[u].size()<(int)blocks[v].size()) swap(u,v),cnt[x][0]=cnt[y][0],cnt[x][1]=cnt[y][1];            id[x]=u;            for (itr it=blocks[v].begin();it!=blocks[v].end();++it)            {                data blk=*it;itr pos=blocks[u].insert(blk).ft;                int x_=blk.ft,y_=blk.sd,st,en,l,r;                if (pos!=blocks[u].begin())                {                    st=(--pos)->sd+1;                    if (st==x_) l=pos->ft;                    else l=x_;                    ++pos;                }else l=x_,st=1;                ++pos;                if (pos!=blocks[u].end())                {                    en=pos->ft-1;                    if (y_==en) r=pos->sd;                    else r=y_;                }else r=y_,en=n;                --pos;                (((((cnt[x][0]+=P-calc(x_-l))%=P)+=P-calc(r-y_))%=P)+=calc(r-l+1))%=P;                (((((cnt[x][1]+=P-calc(en-st+1))%=P)+=calc(x_-st))%=P)+=calc(en-y_))%=P;                if (x_!=l)                {                    itr pos_=--pos;++pos;                    blocks[u].erase(pos_),blocks[u].erase(mkp(x_,y_)),pos=blocks[u].insert(mkp(l,y_)).ft;                }                if (y_!=r)                {                    itr pos_=++pos;--pos;                    blocks[u].erase(pos_),blocks[u].erase(mkp(l,y_)),blocks[u].insert(mkp(l,r));                }            }            blocks[v].clear();        }    ans[x]=(cnt[x][0]+cnt[x][1])%P;}int main(){    freopen("communicate.in","r",stdin),freopen("communicate.out","w",stdout);    n=read();    for (int i=1,x,y;i<n;++i) x=read(),y=read(),insert(x,y),insert(y,x);    all=calc(n),dfs(1,0),inv=quick_power(all,P-2),printf("%d\n",1ll*sum*inv%P);    fclose(stdin),fclose(stdout);    return 0;}
原创粉丝点击