点分治
点分治
P3806 【模板】点分治 1
我们先随意选择一个节点作为根节点 \(\mathit{rt}\),所有完全位于其子树中的路径可以分为两种:
-
一种是经过当前根节点的路径
-
一种是不经过当前根节点的路径。
对于经过当前根节点的路径,又可以分为两种:
- 一种是以根节点为一个端点的路径
- 另一种是两个端点都不为根节点的路径。
而后者又可以由两条属于前者链合并得到。所以,对于枚举的根节点 \(rt\),我们先计算在其子树中且经过该节点的路径对答案的贡献,再递归其子树对不经过该节点的路径进行求解。
在本题中,对于经过根节点 \(\mathit{rt}\) 的路径,我们要求出有多少对节点经过\(\mathit{rt}\),且距离等于\(\mathit{L}\),可以利用容斥原理,经过\(\mathit{rt}\)的任意两个点一定位于不同的子树中,可以表示为以\(\mathit{rt}\)为根的子树中任意两个点\(\mathit{-}\)以\(\mathit{rt}\)子节点为根结点的子树中的任意两个点,再利用双指针即可以求出答案
点分治过程中,每一层的所有递归过程合计对每个点处理一次,假设共递归 \(h\) 层,则总时间复杂度为 \(O(hn)\)。
若我们 每次选择子树的重心作为根节点,可以保证递归层数最少,时间复杂度为 \(O(n\log n)\)。
请注意在重新选择根节点之后一定要重新计算子树的大小,否则一点看似微小的改动就可能会使时间复杂度错误或正确性难以保证。
代码:
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=3e5+10,mod=998244353;
typedef long long ll;
typedef pair<int,int> PII;
int T;
int n,m,k,q;
vector<PII> e[N];
bool del[N];
int mx[N],sz[N],L[N];
int ans[N];
int big[N],small[N];
void solve(int u,int s) //以u为根结点,s为子树的大小
{
int ms=s+1,root=-1;
function<void(int,int)> dfs1 = [&](int u,int par){ //找以u为根结点的子树的重心
sz[u]=1;
mx[u]=0;
for(auto [v,w] : e[u])
{
if(del[v] || v==par) continue;
dfs1(v,u);
sz[u]+=sz[v];
mx[u]=max(mx[u],sz[v]);
}
mx[u]=max(mx[u],s-sz[u]);
if(mx[u]<ms) ms=mx[u],root=u;
};
//root为找到的重心
dfs1(u,-1);
vector<int> d1,d2;
d1.push_back(0);
auto calc = [&](vector<int> &d,int id) -> void{ //双指针求
sort(d.begin(),d.end());
for(int k=1;k<=m;k++){
ll ans=0;
int len=d.size();
int r=len-1;
for(int i=0;i<len;i++)
{
while(r>=0&&d[i]+d[r]>L[k]) --r;
if(i<r&&d[i]+d[r]==L[k]) ans++;
}
if(id==1) small[k]+=ans;
else big[k]+=ans;
}
};
function<void(int,int,int)> dfs2 =[&](int u,int par,int dep){
d1.push_back(dep);
d2.push_back(dep);
for(auto [v,w] : e[u]){
if(del[v]||v==par) continue;
dfs2(v,u,dep+w);
}
};
for(auto [v,w] : e[root]){
if(del[v]) continue;
d2.clear();
dfs2(v,root,w);
calc(d2,1); //容斥原理
}
calc(d1,2);
del[root] =true;
for(auto [v,w] : e[root]){
if(!del[v]) solve(v,sz[v]);
}
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<n;i++)
{
int u,v,w;
cin>>u>>v>>w;
e[u].push_back(make_pair(v,w));
e[v].push_back(make_pair(u,w));
}
for(int i=1;i<=m;i++)
cin>>L[i];
solve(1,n);
for(int i=1;i<=m;i++)
{
ans[i]=big[i]-small[i];
//cout<<ans[i]<<endl;
if(ans[i]) cout<<"AYE"<<endl;
else cout<<"NAY"<<endl;
}
return 0;
}
P4178 Tree
代码:
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=3e5+10,mod=998244353;
typedef long long ll;
typedef pair<int,int> PII;
int T;
int n,m,k,q;
vector<PII> e[N];
bool del[N];
int mx[N],sz[N],ans,L;
void solve(int u,int s)
{
int ms=s+1,root=-1;
function<void(int,int)> dfs1 = [&](int u,int par){
sz[u]=1;
mx[u]=0;
for(auto [v,w] : e[u])
{
if(del[v] || v==par) continue;
dfs1(v,u);
sz[u]+=sz[v];
mx[u]=max(mx[u],sz[v]);
}
mx[u]=max(mx[u],s-sz[u]);
if(mx[u]<ms) ms=mx[u],root=u;
};
dfs1(u,-1);
vector<int> d1,d2;
d1.push_back(0);
auto calc = [&](vector<int> &d){
sort(d.begin(),d.end());
int m=d.size();
int r=m-1;
ll ans=0;
for(int i=0;i<m;i++)
{
while(r>=0&&d[i]+d[r]>L) --r;
if(i<r) ans+=(r-i);
}
return ans;
};
function<void(int,int,int)> dfs2 =[&](int u,int par,int dep){
d1.push_back(dep);
d2.push_back(dep);
for(auto [v,w] : e[u]){
if(del[v]||v==par) continue;
dfs2(v,u,dep+w);
}
};
for(auto [v,w] : e[root]){
if(del[v]) continue;
d2.clear();
dfs2(v,root,w);
ans-=calc(d2);
}
ans+=calc(d1);
del[root] =true;
for(auto [v,w] : e[root]){
if(!del[v]) solve(v,sz[v]);
}
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<n;i++)
{
int u,v,w;
cin>>u>>v>>w;
e[u].push_back(make_pair(v,w));
e[v].push_back(make_pair(u,w));
}
cin>>L;
solve(1,n);
cout<<ans<<endl;
return 0;
}
P4149 [IOI2011] Race
代码:
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=3e5+10,mod=998244353;
typedef long long ll;
typedef pair<int,int> PII;
int T;
int n,m,k,q;
vector<PII> e[N];
bool del[N];
int mx[N],sz[N],ans,L;
void solve(int u,int s) //以u为根结点,s为子树的大小
{
int ms=s+1,root=-1;
function<void(int,int)> dfs1 = [&](int u,int par){ //找以u为根结点的子树的重心
sz[u]=1;
mx[u]=0;
for(auto [v,w] : e[u])
{
if(del[v] || v==par) continue;
dfs1(v,u);
sz[u]+=sz[v];
mx[u]=max(mx[u],sz[v]);
}
mx[u]=max(mx[u],s-sz[u]);
if(mx[u]<ms) ms=mx[u],root=u;
};
//root为找到的重心
dfs1(u,-1);
map<int,int> mp;
mp[0]=0;
for(auto [v,w] : e[root]){
vector<PII> d;
if(del[v]) continue;
function<void(int,int,int,int)> dfs2 =[&](int u,int par,int dep,int dep2){
sz[u]=1;
d.push_back({dep,dep2});
for(auto [v,w] : e[u]){
if(del[v]||v==par) continue;
sz[u]+=sz[v];
dfs2(v,u,dep+w,dep2+1ll);
}
};
dfs2(v,root,w,1ll);
for(auto [d1,d2] : d)
{
if(mp.count(L-d1)) ans=min(ans,mp[L-d1]+d2);
}
for(auto [d1,d2] : d)
{
if(mp.count(d1)) mp[d1]=min(mp[d1],d2);
else mp[d1]=d2;
}
}
del[root] =true;
for(auto [v,w] : e[root]){
if(!del[v]) solve(v,sz[v]);
}
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>L;
ans=n+1;
for(int i=1;i<n;i++)
{
int u,v,w;
cin>>u>>v>>w;
++u,++v;
e[u].push_back(make_pair(v,w));
e[v].push_back(make_pair(u,w));
}
solve(1,n);
if(ans<n+1) cout<<ans<<endl;
else cout<<"-1"<<endl;
return 0;
}