HihoCoder - 1715 树的连通问题

题面在这里!

 

    正式告别文化课回归的第一题QWQ,然鹅半个月之后还是要退役QWQWQWQWQ

 

    好像很久之前就见过的一个题,当时只会打一打 O(N^2) 的暴力QWQ,正好今天又写了遍这个暴力用来对拍23333

    正解的话就是把O(N^2)的暴力用数据结构优化一下。

 

    (按照常规套路,先把树建成有根树,以1为根)

    考虑把原问题建模,每条边的贡献就是这条边 i 的特征0/1数组的区间内不止一种数的区间数,特征数组a[]是指,如果点x在边i下面,那么a[x]=1;否则a[x]=0。

    这个显然补集转化一下会更加简单,于是问题变成了如何快速的求每条边的特征数组的区间内只有一种数的区间数。。。。

 

    明显具有可合并性嘛QWQ,直接把子树的一坨数组合并一下(因为1不可能重),然后把当前扫的根x的a[x]=1就行了。

    直接合并还是O(N^2)的,不过改成线段树合并就只有N带log啦QWQ。。。。。

    写之前搞清楚需要什么信息以及怎么合并就行啦(对于窝这种不爱写数据结构的老年养生选手来说还是写起来比较麻烦的QWQ)。

 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define mid (l+r>>1)
#define LC lc[o]
#define RC rc[o]
const int N=100005,lg=37;

int lc[N*lg],rc[N*lg],lt[N*lg],rt[N*lg],uu,vv;
int cnt,n,m,rot[N],llen[N*lg],rlen[N*lg];
int to[N*2],ne[N*2],hd[N],num=0,le,ri;
ll sum[N*lg],ans;

inline void add(int x,int y){ to[++num]=y,ne[num]=hd[x],hd[x]=num;}

inline void build(int L){
	cnt++,lt[cnt]=rt[cnt]=0;
	llen[cnt]=rlen[cnt]=L;
	sum[cnt]=L*(ll)(L+1)>>1;
}

inline void maintain(int o,int l,int r){
	lt[o]=lt[LC],rt[o]=rt[RC];
	llen[o]=llen[LC]+((llen[LC]==mid-l+1&&lt[LC]==lt[RC])?llen[RC]:0);
	rlen[o]=rlen[RC]+((rlen[RC]==r-mid&&rt[RC]==rt[LC])?rlen[LC]:0);	
	sum[o]=sum[LC]+sum[RC]+(rt[LC]==lt[RC]?rlen[LC]*(ll)llen[RC]:0);
}

void update(int o,int l,int r){
	if(l==r){ sum[o]=llen[o]=rlen[o]=lt[o]=rt[o]=1; return;}
	
	if(!lc[o]) build(mid-l+1),lc[o]=cnt;
	if(!rc[o]) build(r-mid),rc[o]=cnt;
	
	if(le<=mid) update(LC,l,mid);
	else update(RC,mid+1,r);
	
	maintain(o,l,r);
}

int Merge(int x,int y,int l,int r){
	if(!x||!y) return x+y;
	if(l==r||(!rc[x]&&!rc[y]&&!lc[x]&&!lc[y])) return lt[x]?x:y;
	
	if(lc[x]||lc[y]) lc[x]=Merge(lc[x],lc[y],l,mid);
	if(rc[x]||rc[y]) rc[x]=Merge(rc[x],rc[y],mid+1,r);
	
	maintain(x,l,r);
	return x;
}

/*
void B(int o,int l,int r){
	printf("%d %d %d %d %d %d %d %lld\n",o,l,r,lt[o],rt[o],llen[o],rlen[o],sum[o]);
	
	if(l==r) return;
	
	if(lc[o]) printf("%d is son of %d\n",lc[o],o),B(lc[o],l,mid);
	if(rc[o]) printf("%d is son of %d\n",rc[o],o),B(rc[o],mid,r);	
}
*/

void dfs(int x,int fa){
	build(n),rot[x]=cnt;
	
	le=x,update(rot[x],1,n);
	
	for(int i=hd[x];i;i=ne[i])
	    if(to[i]!=fa) dfs(to[i],x),rot[x]=Merge(rot[x],rot[to[i]],1,n);
	
	if(fa) ans-=sum[rot[x]];
	
//	if(x==4) B(rot[x],1,n);
}

int main(){
//	freopen("data.in","r",stdin);
//	freopen("data.out","w",stdout);
	
	scanf("%d",&n),ans=(n-1)*(ll)(n+1)*(ll)n>>1;
	for(int i=1;i<n;i++)
	    scanf("%d%d",&uu,&vv),add(uu,vv),add(vv,uu);
	    
	dfs(1,0);
	printf("%lld\n",ans);
	
	return 0;
}

  

 

posted @ 2018-06-28 21:21  蒟蒻JHY  阅读(270)  评论(0编辑  收藏  举报