入门了一下点分治,发现也并不是很难。
解决的问题一般是树上满足某种条件的点对数。
思路也很直接,首先如果考虑暴力分治求解,对于一个树根来说,满足条件的点对可以分为两类:
- 经过树根
- 不经过树根
那么考虑对其分治,每次只计算第一类经过树根的点对数,然后分治其各个子树即可。
考虑此时的复杂度,如果我们能在
O
(
n
)
O(n)
O(n) 或
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn) 的时间内处理出n个点中的第一类点对个数,那么整体复杂度是
O
(
n
2
)
O(n^2)
O(n2) 及以上的,但如果每次以树的重心为树根进行分治,那么分治层数也就来到了
l
o
g
n
logn
logn 级别,此时复杂度便可以解决一些相应问题。
来一道例题,顺便存一下求重心的板子
hdu5977
题意:一颗树,每个点有一个属性,求能经过所有属性的点的路径个数(起点终点有一个不同即为不同,同一条路径反过来也要算一次),5e4个点,总属性个数不超过10。
考虑点分治的思路,就同最基本的点分治模板求树上路径长度小于等于k的点对数思路一样,既然要找经过所有属性的路径,且属性总个数很小,那么考虑状态压缩表示经过的属性,对于一个树根分治时,将到子树上每个点的路径状态保存下来,再枚举子集求解即可,当然,也要像之前提到的那道题一样,分治时会计算到从同一颗子树的两个点组成的路径,去一下重就好。特别的,当总属性个数等于1时,由于一个点就可以满足要求,但和一般的点分治处理方法无法处理此种情况,特判一下即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> P;
const int maxn=50005;
int inf=0x3f3f3f3f;
vector<int> E[maxn];
vector<int> dis;
int siz[maxn],maxs[maxn],vis[maxn];
int rt,maxx;
long long dp[1<<10],a[maxn];
long long n,k,s;
long long res;
void dfs_siz(int x,int fa){
siz[x]=1;
maxs[x]=0;
for(auto i:E[x]){
if(vis[i] || i==fa) continue;
dfs_siz(i,x);
maxs[x]=max(maxs[x],siz[i]);
siz[x]+=siz[i];
}
}
void dfs_rt(int x,int fa,int top){
maxs[x]=max(maxs[x],siz[top]-siz[x]);
if(maxx>maxs[x]){
rt=x;
maxx=maxs[x];
}
for(auto i:E[x]){
if(vis[i] || i==fa) continue;
dfs_rt(i,x,top);
}
}
void dfs_dis(int x,int fa,int d){
d|=(1<<(a[x]-1));
dis.push_back(d);
dp[d]++;
for(auto i:E[x]){
if(vis[i] || i==fa) continue;
dfs_dis(i,x,d);
}
}
long long cal(int x,int d){
long long ret=0;
memset(dp,0,sizeof(dp));
dis.clear();
dfs_dis(x,x,d);
for(auto i:dis){
dp[i]--;
ret+=dp[s];
for(int j=i;j;j=(j-1)&i){
ret+=dp[s^j];
}
dp[i]++;
}
return ret;
}
void dfz(int x){
maxx=inf;
dfs_siz(x,x);
dfs_rt(x,x,x);
vis[rt]=1;
res+=cal(rt,0);
for(auto i:E[rt]){
if(vis[i]) continue;
res-=cal(i,1<<(a[rt]-1));
}
for(auto i:E[rt]){
if(!vis[i]) dfz(i);
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
while(cin>>n>>k){
res=0;
s=(1<<k)-1;
for(int i=1;i<=n;i++){
vis[i]=0;
maxs[i]=0;
E[i].clear();
}
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<n;i++){
int x,y;
cin>>x>>y;
E[x].push_back(y);
E[y].push_back(x);
}
dfz(1);
if(k==1) cout<<n*n<<'\n';
else cout<<res<<'\n';
}
}
再来一道例题
hdu5314
题目大意:
给一棵树,每个节点有个权值,求路径上最大点权和最小点权差值小于等于D的点对数。
思路:
点分治,维护到根的最大点权和最小点权,然后问题就转化成了一个类似于偏序问题的问题,按最大值排序,逐个插入树状数组,新插入的点最大值一定为该路径中的最大点权,区间查询即可。
代码:
#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef pair<int,int> P;
const int maxn=100005;
int inf=0x3f3f3f3f;
vector<int> E[maxn];
vector<P> dis;
int siz[maxn],maxs[maxn],vis[maxn];
int rt,maxx;
int a[maxn],aa[maxn],l[maxn],r[maxn],b[maxn*3];
long long n,k,s;
long long res;
class bit{public:
long long node[maxn*3];
int lb(int x){return x&(-x);}
void update(int pos,long long val){
if(pos>0)
for(int i=pos;i<=b[0];i+=lb(i))
node[i]+=val;
}
long long ask(int pos){
long long sum=0;
if(pos>0)
for(int i=pos;i;i-=lb(i))
sum+=node[i];
return sum;
}
long long query(int l,int r){
return ask(r)-ask(l-1);
}
}tree;
void dfs_siz(int x,int fa){
siz[x]=1;
maxs[x]=0;
for(auto i:E[x]){
if(vis[i] || i==fa) continue;
dfs_siz(i,x);
maxs[x]=max(maxs[x],siz[i]);
siz[x]+=siz[i];
}
}
void dfs_rt(int x,int fa,int top){
maxs[x]=max(maxs[x],siz[top]-siz[x]);
if(maxx>maxs[x]){
rt=x;
maxx=maxs[x];
}
for(auto i:E[x]){
if(vis[i] || i==fa) continue;
dfs_rt(i,x,top);
}
}
void dfs_dis(int x,int fa,P d){
d.first=max(d.first,a[x]);
d.second=min(d.second,a[x]);
if(b[d.first]-b[d.second]>k) return;
dis.push_back(d);
for(auto i:E[x]){
if(vis[i] || i==fa) continue;
dfs_dis(i,x,d);
}
}
long long cal(int x,P d){
long long ret=0;
dis.clear();
dfs_dis(x,x,d);
sort(dis.begin(),dis.end());
for(auto i:dis){
ret+=tree.query(l[i.first],i.first);
tree.update(i.second,1);
}
for(auto i:dis) tree.update(i.second,-1);
return ret;
}
void dfz(int x){
maxx=inf;
dfs_siz(x,x);
dfs_rt(x,x,x);
vis[rt]=1;
res+=cal(rt,P(0,inf));
for(auto i:E[rt]){
if(vis[i]) continue;
res-=cal(i,P(a[rt],a[rt]));
}
for(auto i:E[rt]){
if(!vis[i]) dfz(i);
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
while(T--){
cin>>n>>k;
for(int i=1;i<=n;i++){
E[i].clear();
vis[i]=0;
}
for(int i=0;i<=b[0];i++) b[i]=0;
b[0]=0;
res=0;
for(int i=1;i<=n;i++){
cin>>aa[i],b[++b[0]]=aa[i];
b[++b[0]]=aa[i]-k;
}
sort(b+1,b+1+b[0]);
b[0]=unique(b+1,b+1+b[0])-b-1;
for(int i=1;i<=n;i++){
a[i]=lower_bound(b+1,b+1+b[0],aa[i])-b;
l[a[i]]=lower_bound(b+1,b+1+b[0],aa[i]-k)-b;
}
for(int i=1;i<n;i++){
int x,y;
cin>>x>>y;
E[x].push_back(y);
E[y].push_back (x);
}
dfz(1);
cout<<res*2<<endl;
}
}