bzoj1791 [Ioi2008]Island 岛屿(求基环树直径,单调队列)

给定一个基环树森林,求每一棵基环树的直径,他们的和就是答案。
基环树的直径:先找到环,然后直径只有可能是
1.环上某一个点的子树的直径
2.环上某两个点之间的距离加上他们各自子树中最深的点的深度。
然后搜一搜就好了,情况2可以单调队列一下。
O ( n ) O(n) O(n)
我写的太丑了,bzoj跑不过去qaq

#include 
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 1000010
int n,h[N],num=1,fa[N],dfn[N],dfnum=0,q[N],qh,qt;
ll mx1[N],mx2[N],ans=0,d[N],dis[N],res=0;
bool f[N];
struct edge{
	int to,nxt,val;
}data[N<<1];
vector<int>cirid;
void findcir(int x,int ii){
	dfn[x]=++dfnum;
	for(int i=h[x];i;i=data[i].nxt){
		if(!cirid.empty()) return;
		if(i==(ii^1)) continue;
		int y=data[i].to;
		if(dfn[y]>dfn[x]) continue;
		d[y]=data[i].val;
		if(!dfn[y]){
			fa[y]=x;findcir(y,i);continue;
		}
		if(dfn[y]<dfn[x]){
			int xx=x;
			while(xx!=fa[y]) cirid.push_back(xx),xx=fa[xx];
		}
	}
	
}
void dfs1(int x,int ii){
	for(int i=h[x];i;i=data[i].nxt){
		if(i==(ii^1)) continue;
		int y=data[i].to;
		if(f[y]) continue;
		dfs1(y,i);
		ll w=mx1[y]+data[i].val;
		if(w>mx1[x]) mx2[x]=mx1[x],mx1[x]=w;
		else if(w>mx2[x]) mx2[x]=w;
	}
	ans=max(ans,mx1[x]+mx2[x]);
}
int main(){
//	freopen("a.in","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		int x,w;scanf("%d%d",&x,&w);
		data[++num].to=i;data[num].nxt=h[x];h[x]=num;data[num].val=w;
		data[++num].to=x;data[num].nxt=h[i];h[i]=num;data[num].val=w;
	}
	for(int x=1;x<=n;++x){
		if(dfn[x]) continue;
		cirid.clear();ans=0;findcir(x,0);
		int m=cirid.size();
		for(int i=0;i<m;++i) f[cirid[i]]=1;
		for(int i=0;i<m;++i) dfs1(cirid[i],0);
		for(int i=0;i<m;++i) cirid.push_back(cirid[i]);
		qh=1,qt=0;
		q[++qt]=0;dis[0]=0;
		for(int i=1;i<cirid.size();++i){
			dis[i]=dis[i-1]+d[cirid[i-1]];
			while(qh<=qt&&q[qh]<i+1-m) ++qh;
			ans=max(ans,mx1[cirid[i]]+dis[i]+mx1[cirid[q[qh]]]-dis[q[qh]]);
			while(qh<=qt&&mx1[cirid[q[qt]]]-dis[q[qt]]<=mx1[cirid[i]]-dis[i]) --qt;
			q[++qt]=i;
		}
		res+=ans;
	}
	printf("%lld\n",res);
	return 0;
}

你可能感兴趣的:(bzoj,单调队列,-----树-------)