支配对
P7880 rldcot
边有边权,\(m\) 次询问一个树上 \([l,r]\) 的节点区间内 \(dep_{lca(i,j)}\) 的种类数。
如果是 \(O(n^2)\) 个点对显然会有重复的信息,既然要求 \(l \le i \le j \le r\),那么对于 lca 相同的点对 \((a,b)\) 和 \((c,d)\) 来说,若 \(a<c,d<b\) 则 \([l,r]\) 包含 \((a,b)\) 就一定会包含 \((c,d)\),那么 \((a,b)\) 这个点对就没有用,称 \((c,d)\) 为支配对。
这类在树上与 lca 相关的支配对一般考虑 dsu on tree,以当前点 \(x\) 为 lca,去收集子树的信息,如果两个点在不同子树里 lca 就是 \(x\),那么按照支配对的思路,任何一个点 \(u\) 应该与之最编号接近的两个点组成点对,我们用一个 set,遍历完重儿子 set 里存好这个子树里的点,先遍历一个轻儿子子树,每个点找出前驱后继作为点对,再把这个子树也加入集合。
得到了 \(O(n \log n)\) 个点对,总时间复杂度 \(O(n \log^2 n)\),再扫描线就可以了。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int n,m;
const int N = 1e5+10;
struct edge{
int v,w;
};
vector<edge>g[N];
struct qy{
int l,r;
ll id;
};
vector<qy>q,p;
bool cmp(qy a,qy b){
return a.r<b.r;
}
ll dep[N],b[N],len;
int fa[N],siz[N],wc[N];
void dfs(int x,int f){
fa[x]=f;
siz[x]=1;
for(auto [v,w]:g[x]){
if(v==f)continue;
dep[v]=dep[x]+w;
dfs(v,x);
siz[x]+=siz[v];
if(siz[v]>siz[wc[x]])wc[x]=v;
}
}
set<int>s;
int pre(int x){
auto it=s.lower_bound(x);
if(it==s.begin())return 0;
it--;
return *it;
}
int nxt(int x){
auto it=s.upper_bound(x);
if(it==s.end())return 0;
return *it;
}
void DFS(int x,int f,int c){
int e=pre(x);
if(e)p.push_back({e,x,dep[c]});
e=nxt(x);
if(e)p.push_back({x,e,dep[c]});
for(auto [v,w]:g[x]){
if(v==f)continue;
DFS(v,x,c);
}
}
void DFS2(int x,int f,int c){
s.insert(x);
for(auto [v,w]:g[x]){
if(v==f)continue;
DFS2(v,x,c);
}
}
void dot(int x,int f){
for(auto [v,w]:g[x]){
if(v==f||v==wc[x])continue;
dot(v,x);
s.clear();
}
if(wc[x])dot(wc[x],x);
int e=pre(x);
if(e)p.push_back({e,x,dep[x]});
e=nxt(x);
if(e)p.push_back({x,e,dep[x]});
s.insert(x);
for(auto [v,w]:g[x]){
if(v==f||v==wc[x])continue;
DFS(v,x,x);
DFS2(v,x,x);
}
}
int lowbit(int x){
return x&(-x);
}
int t[N],lst[N],ans[5*N];
void add(int p,int v){
for(int i=p;i<=n;i+=lowbit(i)){
t[i]+=v;
}
}
int ask(int p){
int res=0;
for(int i=p;i;i-=lowbit(i)){
res+=t[i];
}
return res;
}
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;
g[u].push_back({v,w});
g[v].push_back({u,w});
}
dfs(1,0);
for(int i=1;i<=n;i++){
b[++len]=dep[i];
}
sort(b+1,b+1+len);
len=unique(b+1,b+1+len)-b-1;
for(int i=1;i<=n;i++){
dep[i]=lower_bound(b+1,b+1+len,dep[i])-b;
}
for(int i=1;i<=m;i++){
int l,r;
cin>>l>>r;
q.push_back({l,r,i});
}
dot(1,0);
for(int i=1;i<=n;i++){
p.push_back({i,i,dep[i]});
}
sort(p.begin(),p.end(),cmp);
sort(q.begin(),q.end(),cmp);
int cur=0;
for(auto [l,r,id]:q){
while(cur<p.size()&&p[cur].r<=r){
int col=p[cur].id;
if(!lst[col]){
lst[col]=p[cur].l;
add(lst[col],1);
}else if(p[cur].l>lst[col]){
add(lst[col],-1);
lst[col]=p[cur].l;
add(lst[col],1);
}
cur++;
}
ans[id]=ask(r)-ask(l-1);
}
for(int i=1;i<=m;i++){
cout<<ans[i]<<endl;
}
return 0;
}
[Ynoi2004] rpmtdq
这题询问编号区间 \([l,r]\) 中距离最小的两点距离,与树上的路径相关,想到点分治,因为一条路径必定会经过一个分治中心,设当前分治层内节点 \(u\) 到中心的距离为 \(d_u\),那么 \(u,v\) 两点间的距离可以表示为 \(d_u+d_v\),尽管 \(u,v\) 在同一个子树内时这个距离就不是真实距离,但是我们能在之后的分治层中保证他是真实距离。
考虑怎么样的点对会成为支配对,对于 \(i < j< k\),如果 \(d_j<d_k\),那么显然 \(k\) 就被支配了,那如果 \(d_j>d_k\) 呢?可能 \((j,k)\) 会作为支配对把 \((j,i)\) 支配,也可能这两对都能贡献答案。
每一层可以得到很多二元组 \((i,d_i)\),按 \(i\) 排序,用单调栈处理出每一个 \(i\) 前面第一个比他小的数和后面第一个比他小的数,作为可贡献的点对加入最终集合中,那么每一层 \(O(n)\) 个点对,总共就是 \(O(n \log n)\) 个点对。
老样子和询问一起做扫描线,取一个后缀最小值就好了。
复杂度 \(O(n \log^2 n+q \log n)\)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,ll>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int n,m;
const int N = 2e5+10;
const ll inf=1e17;
struct edge{
int v;
ll w;
};
vector<edge>g[N];
struct qy{
int l,r;
ll id;
};
vector<qy>q,p;
bool cmp(qy a,qy b){
return a.r<b.r;
}
ll dep[N],fa[N],siz[N],wc[N],top[N],vis[N],maxp[N],tot,rt;
ll d[N];
void dfs(int x,int f){
dep[x]=dep[f]+1;
fa[x]=f;
siz[x]=1;
for(auto [v,w]:g[x]){
if(v==f)continue;
d[v]=d[x]+w;
dfs(v,x);
siz[x]+=siz[v];
if(siz[v]>siz[wc[x]])wc[x]=v;
}
}
void dfs2(int x,int t){
top[x]=t;
if(wc[x]){
dfs2(wc[x],t);
for(auto [v,w]:g[x]){
if(v!=wc[x]&&v!=fa[x])dfs2(v,v);
}
}
}
int lca(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<=dep[top[y]])swap(x,y);
x=fa[top[x]];
}
if(dep[x]<dep[y])return x;
return y;
}
ll dis(int x,int y){
return d[x]+d[y]-2*d[lca(x,y)];
}
pii st[N],st2[N];
int top1,top2;
void getct(int u,int f){
siz[u]=1;
maxp[u]=0;
for(auto [v,w]:g[u]){
if(v==f||vis[v])continue;
getct(v,u);
siz[u]+=siz[v];
maxp[u]=max(maxp[u],siz[v]);
}
maxp[u]=max(maxp[u],tot-siz[u]);
if(maxp[u]<maxp[rt])rt=u;
}
void getdis(int x,int f,ll ds){
siz[x]=1;
st[++top1]={x,ds};
for(auto [v,w]:g[x]){
if(vis[v]||v==f)continue;
getdis(v,x,ds+w);
siz[x]+=siz[v];
}
}
void push(int u,int v){
if(u>v)swap(u,v);
p.push_back({u,v,dis(u,v)});
}
void solve(int x){
top1=0;
getdis(x,0,0);
sort(st+1,st+1+top1);
top2=0;
for(int i=1;i<=top1;i++){
int id=st[i].fst;
ll d=st[i].scd;
while(top2&&d<st2[top2].scd)top2--;
if(top2)push(id,st2[top2].fst);
st2[++top2]=st[i];
}
top2=0;
for(int i=top1;i>=1;i--){
int id=st[i].fst;
ll d=st[i].scd;
while(top2&&d<st2[top2].scd)top2--;
if(top2)push(id,st2[top2].fst);
st2[++top2]=st[i];
}
}
void ctd(int x){
vis[x]=1;
solve(x);
for(auto [v,w]:g[x]){
if(vis[v])continue;
tot=siz[v];
maxp[rt=0]=N;
getct(v,0);
ctd(rt);
}
}
int lowbit(int x){
return x&(-x);
}
ll t[N],ans[5*N];
void add(int p,ll v){
for(int i=p;i;i-=lowbit(i)){
t[i]=min(t[i],v);
}
}
ll query(int p){
ll res=inf;
for(int i=p;i<=n;i+=lowbit(i)){
res=min(res,t[i]);
}
return res;
}
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;
ll w;
cin>>u>>v>>w;
g[u].push_back({v,w});
g[v].push_back({u,w});
}
dfs(1,0);
dfs2(1,1);
maxp[rt=0]=N;
tot=n;
getct(1,0);
ctd(rt);
cin>>m;
for(int i=1;i<=m;i++){
int l,r;
cin>>l>>r;
if(l>=r)ans[i]=-1;
else q.push_back({l,r,i});
}
for(int i=1;i<=n;i++)t[i]=inf;
sort(p.begin(),p.end(),cmp);
sort(q.begin(),q.end(),cmp);
//cout<<p.size()<<endl;
int cur=0;
for(auto [l,r,id]:q){
while(cur<p.size()&&p[cur].r<=r){
add(p[cur].l,p[cur].id);
cur++;
}
ans[id]=query(l);
}
for(int i=1;i<=m;i++){
cout<<ans[i]<<endl;
}
return 0;
}

浙公网安备 33010602011771号