【学习笔记】点分治
P3086
点分治可以将O(n^2)优化成O(nlogn)
核心操作
每次把树存入,找出重心作为root,动态遍历子树每个点到root距离,再判断前面的子树是否可以与当前子树结合=k。给root打上重心标记记作删除,然后回退清空
复杂度
共log层,每层遍历整张图,复杂度O(nlogn)
代码
#include<bits/stdc++.h>
#define Pair pair<int,int>
#define w first
#define to second
using namespace std;
const int N=1e4+10,Q=1e7+10;int n,m;
bool aaa;
vector<Pair> mp[N];
int ask[N];int ans[N];
int cnt;
int del[N];//是否被删除
int siz[N];
int mson[N];
void fsiz(int u,int fa){
cnt++;
siz[u]=1;
for(auto e:mp[u]){
int v=e.to;
if(v==fa||del[v]) continue;
fsiz(v,u);
siz[u]+=siz[v];
}
}
int root;
void Root(int u,int fa){
mson[u]=cnt-siz[u];
for(auto e:mp[u]){
int v=e.to;
if(v==fa||del[v]) continue;
Root(v,u);
mson[u]=max(mson[u],siz[v]);
}
if(root==-1||mson[u]<mson[root]) root=u;
}
bool hav[Q];//数值是否存在
vector<int> nown;
void get(int u,int fa,int dis){//计算当前子树数值
nown.push_back(dis);
for(auto e:mp[u]){
int v=e.to,w=e.w;
if(v==fa||del[v]) continue;
get(v,u,dis+w);
}
}
void solve(int u){
//获取重心
cnt=0;fsiz(u,u);
root=-1;Root(u,u);
//子树处理
hav[0]=1;
u=root;
for(auto e:mp[u]){
int v=e.to,w=e.w;
if(del[v]) continue;
int beg=nown.size();
get(v,u,w);
for(int i=1;i<=m;i++){
for(int j=beg;j<nown.size()&&(!ans[i]);j++){
if(ask[i]>=nown[j]) ans[i]|=hav[ask[i]-nown[j]];
}
}
for(int i=beg;i<nown.size();i++){
if(nown[i]<Q) hav[nown[i]]=1;
}
}
//回退
while(nown.size()){
if(nown.back()<Q)hav[nown.back()]=0;
nown.pop_back();
}
del[u]=1;
for(auto e:mp[u]){
int v=e.to;
if(del[v]) continue;
solve(v);
}
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<n;i++){
int a,b,c;cin>>a>>b>>c;
mp[a].push_back({c,b});
mp[b].push_back({c,a});
}
for(int i=1;i<=m;i++) cin>>ask[i];
solve(1);
for(int i=1;i<=m;i++) cout<<(ans[i]?"AYE\n":"NAY\n");
return 0;
}
代码易错点(警示后人)
- 第15行,cnt++
- 第51行,u=root

浙公网安备 33010602011771号