点 分 治

POJ 1741
做到这题就像怎么做,想了半个小时网上搜下思路,发现是点分治板子题了属于是;
点分治,是在一棵树上的处理方法,处理什么问题我也不知道,主要步骤如下:
1.找重心;
2.统计合法方案;
3.递归找接下来的方案;
方案分成三类:
1.穿过重心的;
2.圈地自萌的;
3.停在重心的,需要去特判一下;
不同的点分治题目其区别在于统计的时候方法不同;

AcWing 252

就是板子题,直接上代码看下了;
首先这份代码看起来十分舒服,因为好几块内容分装了,函数拆的很好;

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 10010, M = N * 2;
int n,m;
int h[N],e[M],w[M],ne[M],idx;
bool st[N];
int p[N],q[N];//P是长久队列Q是暂时队列
void add(int a,int b,int c){
	e[idx] = b, w[idx] = c, ne[idx] = h[a] ++ ;
}//加边函数
int get_size(int u,int fa){
	if(st[u]) return 0;
	int res = 1;
	for(int i = h[u]; ~i; i = ne[i])
		if(e[i] != fa)
			res += get_size(e[i],u);
	return res;
}//这个函数的功能就跟他的名字所说的是一样的
int get_wc(int u,int fa,int tot, int& wc){
	if(st[u]) return 0;
	int sum = 1, ms = 0;//sum一定要等于1不然会死
	for(int i = h[u]; ~i; i = ne[i]){
		int j = e[i]; 
		if(j == fa) continue;
		int t = get_wc(j,u,tot,wc);
		ms = max(ms,t);		sum += t;
	}//和求size的方式有点像哦
	ms = max(ms,tot - sum);
	if(ms <= tot/2 ) wc = u;
	return sum;
}//找重心函数,是直接传参传回去的,当然这个找的不是严格的重心
//只是符合性质的一个节点
void get_dist(int u ,int fa,int dist, int& qt){
	if (st[u]) return;
	q[qt++] = dist;
	for(int i = h[u]; ~i; i = ne[i]){
		if(e[i] != fa) get_dist(e[i], u, dist + w[i], qt);
	}
}//深搜因为好找
int get(int a[],int k){
	sort(a,a+k);
	int res = 0;
	for(int i = k - 1, j = -1; i >= 0; i--){
		while(j+1<i && a[j+1] + a[i] <= m) j ++ ;
		j = min(j, i-1) ;
		res += j+1;
	}
	return res;
}//尺取法
int calc(int u){
	if(st[u]) return 0;
	int res = 0;
	get_wc(u,-1,get_size(u,-1),u);
	st[u] = true;
	int pt = 0;
	for(int i=h[u];~i;i = ne[i]){
		int j = e[i], qt = 0;
		get_dist(j,-1,w[i],qt);
		res -= get(q,qt);//容斥原理
		for(int k = 0;k < qt;k++){
			if(q[k] <= m) res++;
			p[pt++] = q[k];
		}
	}
	res += get(p,pt);
	for(int i = h[u];~i;i = ne[i]) res += calc(e[i]);
	return res;
}//别的没啥好说了。
int main()
{
	while(scanf("%d%d",&n,&m), n || m){
		memset(st,0,sizeof st);
		memset(h,-1,sizeof h);
		idx = 0;
		for(int i = 0; i < n-1 ; i++){
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);
			add(a,b,c);		add(b,a,c);
		}
		printf("%d\n",calc(0));
	}
	
	return 0;
}

再来个例题;

AcWing 264

和上一题大体相同就是归并部分不一样;

#inclulde<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef pair<int,int> PII;
const int N = 200010, M = N * 2,S = 1000010, INF = 0x3f3f3f3f;
int n,m;
int h[N],e[M],w[M],ne[M],idx;
int f[S],ass = INF;
PII p[N],q[N];
bool st[N];
void add(int a,int b,int c){
	e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx ++ ;
}
int get_size(int u,int fa){
	if(st[u]) return 0;
	int res = 1;
	for(int i=h[u]; ~i;i = ne[i]){
		int j = e[i];
		if(j == fa) continue;
		res += get_size(j,u);
	}
	return res;
}int get_wc(int u,int fa,int tot,int& wc){
	if(st[u]) return 0;
	int sum = 1,ms = 0;
	for(int i = h[u]; ~i; i = ne[i]){
		int j = e[i];
		if(j == fa) continue;
		int t = get_wc(j,u,tot,wc);
		ms = max(ms,t);
		sum += t;
	}
	ms = max(ms,tot - sum);
	if( ms <= tot/2 ) wc = u;
	return sum;
}void get_dist(int u,int fa,int dist,int cnt,int& qt){
	if(st[u] || dist > m) return;
	q[qt++] = {dist, cnt};
	for(int i = h[u]; ~i;i = ne[i]) {
		int j = e[i];
		if(j == fa) continue; 
		get_dist(j,u,dist+w[i],cnt+1,qt);
	}
}void calc(int u){
	if(st[u]) return;
	get_wc(u,-1,get_size(u,-1),u);
	st[u] = true;
	int pt = 0;
	for(int i = h[u]; ~i; i = ne[i]){
		int j = e[i], qt = 0;
		get_dist(j,u,w[i],1,qt);
		for(int k = 0; k <qt; k ++){
			auto& t = q[k];
			if( t.first == m) ass = min(ass,t.second);
			ass = min(ass,f[m-t.first] + t.second);
			p[pt++] = t;
		}
		for(int k = 0; k < qt; k ++){
			auto& t = q[k];
			f[t.first] = min(f[t.first], t.second);
		}
	}
	for(int i =0 ;i <pt; i++)
		f[p[i].first] = INF;
	for(int i = h[u];~i; i = ne[i]) calc(e[i]);
}
int main()
{
	scanf("%d%d",&n,&m);
	memset(h,-1,sizeof h);
	for(int i = 0;i < n-1; i++){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,c),  add(b,a,c);
	}
	memset(f,0x3f,sizeof f);
	calc(0);
	
	if(ass == INF) ass= -1;
	printf("%d\n",ass);
	
	return 0;
}
上一篇:kafka 发送大量数据 超时


下一篇:使用 PicGo + sm.ms + Typora编辑器 搭建个人图床