P3806 【模板】点分治 (模板)

题目背景

感谢 hzwer 的点分治互测。

题目描述

给定一棵有 nn 个点的树,询问树上距离为 kk 的点对是否存在。

输入格式

第一行两个数 n,mn,m。

第 22 到第 nn 行,每行三个整数 u, v, wu,v,w,代表树上存在一条连接 uu 和 vv 边权为 ww 的路径。

接下来 mm 行,每行一个整数 kk,代表一次询问。

输出格式

对于每次询问输出一行一个字符串代表答案,存在输出 AYE,否则输出 NAY

输入输出样例

输入 #1复制

2 1
1 2 2
2

输出 #1复制

AYE

说明/提示

数据规模与约定

  • 对于 30\%30% 的数据,保证 n\leq 100n≤100。
  • 对于 60\%60% 的数据,保证 n\leq 1000n≤1000,m\leq 50m≤50 。
  • 对于 100\%100% 的数据,保证 1 \leq n\leq 10^41≤n≤104,1 \leq m\leq 1001≤m≤100,1 \leq k \leq 10^71≤k≤107,1 \leq u, v \leq n1≤u,v≤n,1 \leq w \leq 10^41≤w≤104。

提示

  • 本题不卡常
  • 如果您 #7 一直 RE/TLE,不妨看看 这个帖子
    请注意,第一篇题解也存在帖子中说明的问题,但是鉴于此问题与点分治算法本身无关,并且题解质量非常高,因此暂不撤下题解,仅供参考。

 

根据b站AgOH代码改写

实现离线非n2操作

#include<bits/stdc++.h>
const int maxn = 1e5 + 5;
const int maxm = 1e7 + 5;
const int limit = 1e7;
using namespace std;
vector<pair<int, int>> G[maxn];
int n, m, rt, sum, cnt, q[maxn];
int tmp[maxn], siz[maxn], dis[maxn], maxp[maxn];
bool judge[maxm], ans[maxm], vis[maxm];

void getrt(int x, int f){
	siz[x] = 1;
	maxp[x] = 0;
	for(auto i : G[x]){
		if(vis[i.first] || i.first == f)
			continue;
		getrt(i.first, x);
		siz[x] += siz[i.first];
		if(siz[i.first] > maxp[x])
			maxp[x] = siz[i.first];
	}
	maxp[x] = max(maxp[x], sum - siz[x]);
	if(maxp[x] < maxp[rt]) rt = x;
}

void getdis(int x, int f){
	if(dis[x] <= limit) tmp[cnt++] = dis[x];
	for(auto i : G[x]){
		if(i.first == f || vis[i.first])
			continue;
		dis[i.first] = dis[x] + i.second;
		getdis(i.first, x);
	}
}

void solve(int x){
	queue<int> Q;
	for(auto i : G[x]){
		if(vis[i.first])
			continue;
		cnt = 0;
		dis[i.first] = i.second;
		getdis(i.first, x);
		for(int j = 0; j < cnt; j++)
			for(int k = 0; k < m; k++)
				if(q[k] >= tmp[j])
					ans[k] |= judge[q[k] - tmp[j]];
		for(int j = 0; j < cnt; j++){
			Q.push(tmp[j]);
			judge[tmp[j]] = 1;
		}
	}
	while(!Q.empty()){
		judge[Q.front()] = 0;
		Q.pop();
	}
}

void divide(int x){
	vis[x] = judge[0] = 1;
	solve(x);
	for(auto i : G[x]){
		if(vis[i.first])
			continue;
		maxp[rt = 0] = sum = siz[i.first];
		getrt(i.first, 0);
		getrt(rt, 0);
		divide(rt);
	}
}

int main(){
	ios::sync_with_stdio(0);
	cin >> n >> m;
	for(int i = 1; i < n; i++){
		int a, b, c;
		cin >> a >> b >> c;
		G[a].push_back({b, c});
		G[b].push_back({a, c});
	}
	for(int i = 0; i < m; i++)
		cin >> q[i];
	maxp[0] = sum = n;
	getrt(1, 0);
	getrt(rt, 0);
	divide(rt);
	for(int i = 0; i < m; i++){
		if(ans[i])
			cout << "AYE" << endl;
		else
			cout << "NAY" << endl;
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值