#4013. tree

点分治但有改变
统计时统计每棵子树上路径和为X出现的一次和多次的次数,
同时注意删边可用set优化为logN
本题是点分治的第二种写法

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>

#define Max_N 100005
using namespace std;

typedef set<pair<int, int> >::const_iterator iter;
int tree_size[Max_N];
set<pair<int, int> > E[Max_N];
int compute_subtree_size(int u,int p){
	int res=1;
	for(iter i=E[u].begin();i!=E[u].end();++i){
		if(i->first!=p){
			res+=compute_subtree_size(i->first,u);
		}
	}
	return tree_size[u]=res;
}
int b_offset;
int c_offset;
pair<int, int> B[2*Max_N+1];
pair<int, int> C[2*Max_N+1];
void count_partials(int u,int p,int w,int mxw,int mnw){
	if(w==mxw||w==mnw){
		mxw=max(mxw,w+1);
		mnw=min(mnw,w-1);
		C[c_offset+w].second++;
    }else{
    	C[c_offset+w].first++;
    }
    for(iter i=E[u].begin();i!=E[u].end();++i){
    	if(i->first!=p){
    		count_partials(i->first,u,w+i->second,mxw,mnw);
    	}
    }
}
long long solve(int u){
	int N=compute_subtree_size(u,-1);
	int p=-1;
	for(;;){
		bool seperator=true;
		for(iter i=E[u].begin();i!=E[u].end();++i){
			if(i->first!=p&&tree_size[i->first]>=N/2){
				seperator=false;
				p=u;
				u=i->first;
			    break;
			}
		}
		if(seperator)break;
	}
	b_offset=N;
	fill(B,B+2*N+1,make_pair(0,0));
	B[b_offset].second=1;
	long long result=0;
	for(iter i=E[u].begin();i!=E[u].end();++i){
		int v=i->first;
		c_offset=tree_size[v];
		fill(C,C+2*c_offset+1,make_pair(0,0));
		count_partials(v,u,i->second,i->second,i->second);
		for(int w=-tree_size[v];w<=tree_size[v];++w){
			result+=(long long)C[c_offset+w].first*B[b_offset-w].first;
			result+=(long long)C[c_offset+w].second*B[b_offset-w].first;
			result+=(long long)C[c_offset+w].first*B[b_offset-w].second;
		}
		result+=(long long)C[c_offset].second*(B[b_offset].second-1);
		for(int w=-tree_size[v];w<=tree_size[v];++w){
			B[b_offset+w].first+=C[c_offset+w].first;
			B[b_offset+w].second+=C[c_offset+w].second;
		}
	}
	for(iter i=E[u].begin();i!=E[u].end();++i){
		int v=i->first;
		E[v].erase(make_pair(u,i->second));
		result+=solve(v);
	}
	return result;
}
int main(){
	ios::sync_with_stdio(false);
	int n;cin>>n;
	for(int i=1;i<n;i++){
		int x,y,z;
		cin>>x>>y>>z;
		z=z==0?-1:1;
		E[x].insert(make_pair(y,z));
		E[y].insert(make_pair(x,z));
	}
	cout<<solve(1)<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值