zoukankan      html  css  js  c++  java
  • 51nod2626-未来常数【树上启发式合并,线段树】

    正题

    题目链接:http://www.51nod.com/Challenge/Problem.html#problemId=2626


    题目大意

    给出(n)个点的一棵树,每个区间([l,r])的代价是选出这个区间中的一个点(x)使得它走到所有点然后又回到(x)的路程最短长度,求一个随机区间的期望代价。

    (1leq nleq 10^5)


    解题思路

    考虑统计每条边的贡献,一条边会被记入当且仅当分成的两个树各存在一个点在区间中。

    考虑怎么统计这个贡献,计在两棵树中的点分别为(0)(1),那么合法区间就是包含至少一个(1)和一个(0)的区间,用线段树统计只包含(0)(1)的区间减去即可。

    然后在树上的问题,所以直接上dsu on tree就好了。

    时间复杂度:(O(nlog^2n))

    然后写完题解突然发现线段树合并好像也行而且更快(?


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const ll N=1e5+10,P=1e9+7;
    struct node{
    	ll to,next;
    }a[N<<1];
    ll n,tot,ans,ls[N],siz[N],son[N];
    ll w[N<<2],l0[N<<2],r0[N<<2],l1[N<<2],r1[N<<2];
    void Merge(ll x,ll L,ll R){
    	ll mid=(L+R)>>1;
    	w[x]=w[x*2]+w[x*2+1]+r0[x*2]*l0[x*2+1]+r1[x*2]*l1[x*2+1];
    	l0[x]=(l0[x*2]==mid-L+1)*l0[x*2+1]+l0[x*2];
    	r0[x]=(r0[x*2+1]==R-mid)*r0[x*2]+r0[x*2+1];
    	l1[x]=(l1[x*2]==mid-L+1)*l1[x*2+1]+l1[x*2];
    	r1[x]=(r1[x*2+1]==R-mid)*r1[x*2]+r1[x*2+1];
    	return;
    }
    void Build(ll x,ll L,ll R){
    	if(L==R){w[x]=r0[x]=l0[x]=1;return;}
    	ll mid=(L+R)>>1;
    	Build(x*2,L,mid);
    	Build(x*2+1,mid+1,R);
    	Merge(x,L,R);
    	return;
    }
    void Change(ll x,ll L,ll R,ll pos){
    	if(L==R){swap(l0[x],l1[x]);swap(r0[x],r1[x]);return;}
    	ll mid=(L+R)>>1;
    	if(pos<=mid)Change(x*2,L,mid,pos);
    	else Change(x*2+1,mid+1,R,pos);
    	Merge(x,L,R);return;
    }
    void addl(ll x,ll y){
    	a[++tot].to=y;
    	a[tot].next=ls[x];
    	ls[x]=tot;return;
    }
    void dfs(ll x,ll fa){
    	siz[x]=1;
    	for(ll i=ls[x];i;i=a[i].next){
    		ll y=a[i].to;
    		if(y==fa)continue;
    		dfs(y,x);siz[x]+=siz[y];
    		if(siz[y]>siz[son[x]])son[x]=y;
    	}
    	return;
    }
    void calc(ll x,ll fa){
    	Change(1,1,n,x);
    	for(ll i=ls[x];i;i=a[i].next){
    		ll y=a[i].to;
    		if(y==fa)continue;
    		calc(y,x);
    	}
    	return;
    }
    void solve(ll x,ll fa,ll top){
    	for(ll i=ls[x];i;i=a[i].next){
    		ll y=a[i].to;
    		if(y==fa||y==son[x])continue;
    		solve(y,x,y);
    	}
    	if(son[x])solve(son[x],x,top);
    	Change(1,1,n,x);
    	for(ll i=ls[x];i;i=a[i].next){
    		ll y=a[i].to;
    		if(y==fa||y==son[x])continue;
    		calc(y,x);
    	}
    	(ans+=(n*(n+1)/2-w[1])%P)%=P;
    	if(x==top)calc(x,fa);
    	return;
    }
    ll power(ll x,ll b){
    	ll ans=1;
    	while(b){
    		if(b&1)ans=ans*x%P;
    		x=x*x%P;b>>=1;
    	}
    	return ans;
    }
    signed main()
    {
    	scanf("%lld",&n);
    	for(ll i=1;i<n;i++){
    		ll x,y;
    		scanf("%lld%lld",&x,&y);
    		addl(x,y);addl(y,x);
    	}
    	Build(1,1,n);
    	dfs(1,1);
    	solve(1,1,0);
    	printf("%lld
    ",ans*2*power(n*(n+1)/2%P,P-2)%P);
    	return 0;
    }
    
  • 相关阅读:
    zmap zgrab 环境搭建
    RF是如何工作的?
    RF的优缺点
    国内NLP的那些人那些会
    B-、B+、B*树
    关于LDA的gibbs采样,为什么可以获得正确的样本?
    LDA算法里面Dirichlet分布的两个参数alpha和beta怎样确定?
    如何确定LDA的主题个数
    SMO算法精解
    奇异值与主成分分析(PCA)
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15349776.html
Copyright © 2011-2022 走看看