1. 程式人生 > >【BZOJ3697】采藥人的路徑 點分治

【BZOJ3697】采藥人的路徑 點分治

-s 工作 microsoft tin 類型 head 相等 esc brush

【BZOJ3697】采藥人的路徑

Description

采藥人的藥田是一個樹狀結構,每條路徑上都種植著同種藥材。
采藥人以自己對藥材獨到的見解,對每種藥材進行了分類。大致分為兩類,一種是陰性的,一種是陽性的。
采藥人每天都要進行采藥活動。他選擇的路徑是很有講究的,他認為陰陽平衡是很重要的,所以他走的一定是兩種藥材數目相等的路徑。采藥工作是很辛苦的,所以他希望他選出的路徑中有一個可以作為休息站的節點(不包括起點和終點),滿足起點到休息站和休息站到終點的路徑也是陰陽平衡的。他想知道他一共可以選擇多少種不同的路徑。

Input

第1行包含一個整數N。
接下來N-1行,每行包含三個整數a_i、b_i和t_i,表示這條路上藥材的類型。

Output

輸出符合采藥人要求的路徑數目。

Sample Input

7
1 2 0
3 1 1
2 4 0
5 2 0
6 3 1
5 7 1

Sample Output

1

HINT

對於100%的數據,N ≤ 100,000。

題解:可能我以前學的是假的點分治~

註意一點:起點和終點相同,休息點不同的路徑,算作相同的路徑。

當我們以x為分治中心時,我們遍歷x的所有子樹,並令f[i][0/1]表示在之前掃過的子樹中,到x的路徑上的陽-陰=i,不存在(存在)一個休息點的點數,g[i][0/1]表示在當前的子樹中,到x的路徑上的陽-陰=i,不存在(存在)一個休息站的點數。那麽用f和g來更新答案就行了。別忘了算上起點為x的情況。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=100010;
typedef long long ll;
int n,cnt,root,mx,maxx,tot,d;
ll ans;
int to[maxn<<1],next[maxn<<1],val[maxn<<1],head[maxn],dep[maxn],siz[maxn],vis[maxn],s[maxn];
int f[maxn<<1][2],g[maxn<<1][2];
int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
void getr(int x,int fa)
{
	siz[x]=1;
	int mx=0;
	for(int i=head[x];i!=-1;i=next[i])
	{
		if(vis[to[i]]||to[i]==fa)	continue;	
		getr(to[i],x),siz[x]+=siz[to[i]],mx=max(mx,siz[to[i]]);
	}
	if(max(tot-siz[x],mx)<maxx)	root=x,maxx=max(tot-siz[x],mx);
}
void getd(int x,int fa)
{
	d=max(d,max(dep[x],-dep[x]));
	if(s[dep[x]+maxn])	g[dep[x]+maxn][1]++;
	else	g[dep[x]+maxn][0]++;
	s[dep[x]+maxn]++;
	for(int i=head[x];i!=-1;i=next[i])
	{
		if(vis[to[i]]||to[i]==fa)	continue;
		dep[to[i]]=dep[x]+val[i],getd(to[i],x);
	}
	s[dep[x]+maxn]--;
}
void dfs(int x)
{
	vis[x]=1;
	int i,j,dd=0;
	for(i=head[x];i!=-1;i=next[i])
	{
		if(vis[to[i]])	continue;
		dep[to[i]]=val[i],d=0,getd(to[i],x),dd=max(dd,d);
		for(j=maxn-d;j<=maxn+d;j++)	ans+=(ll)f[2*maxn-j][0]*g[j][1]+f[2*maxn-j][1]*g[j][0]+f[2*maxn-j][1]*g[j][1];
		ans+=(ll)f[maxn][0]*g[maxn][0]+g[maxn][1];
		for(j=maxn-d;j<=maxn+d;j++)	f[j][0]+=g[j][0],f[j][1]+=g[j][1],g[j][0]=g[j][1]=0;
	}
	for(i=maxn-dd;i<=maxn+dd;i++)	f[i][0]=f[i][1]=0;
	for(i=head[x];i!=-1;i=next[i])
	{
		if(vis[to[i]])	continue;
		maxx=1<<30,tot=siz[to[i]],getr(to[i],x),dfs(root);
	}
}
void add(int a,int b,int c)
{
	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
int main()
{
	n=rd();
	int i,a,b,c;
	memset(head,-1,sizeof(head));
	for(i=1;i<n;i++)	a=rd(),b=rd(),c=rd()*2-1,add(a,b,c),add(b,a,c);
	maxx=1<<30,tot=n,getr(1,0),dfs(root);
	printf("%lld",ans);
	return 0;
}

【BZOJ3697】采藥人的路徑 點分治