P7880 [Ynoi2006] rldcot 题解
题目描述
给定一棵 \(n\) 个节点的树, \(1\) 号点为根,边有边权 \(w_i\) 。
\(m\) 次询问,给定 \(l,r\) ,对所有满足 \(l\le i\le j\le r\) 的点对 \((i,j)\) ,求本质不同的 \(dep(lca(i,j))\) 个数。
数据范围
- \(1\le n\le 10^5,1\le m\le 5\cdot 10^5,-10^9\le d\le 10^9,1\le l\le r\le n\) 。
时间限制 \(\texttt{1s}\) ,空间限制 \(\texttt{512MB}\) 。
分析
容易发现 \(dep\) 并没有什么用,因此本题等价于区间虚树数颜色。
显然我们没办法维护虚树的形态,只能维护每个点是否在虚树上出现。
定义三元组 \((l,r,x)\) 表示,若询问区间包含 \([l,r]\) ,那么 \(x\) 可以产生贡献。
那么一个 \(x\) 会对应若干个三元组 \((l,r,x)\) ,\(x\) 产生贡献当且仅当询问区间包含任意一个 \([l,r]\) 。
我们希望这样的刻画方式是充要的,有一个保证充要但是显然会超时的方法:
对每个节点 \(x\) ,将 \(x\) 子树中所有点放到数轴上,对于相邻且不在同一子树的两点 \((l,r)\) ,构建三元组 \((l,r,x)\) 。
构建三元组的代价为 \(\mathcal O(n^2)\) ,但是注意到三元组个数不超过轻子树大小总和,所以三元组个数只有 \(\mathcal O(n\log n)\) !
接下来目标是快速找到每一个三元组,考虑启发式合并:
用
set维护重子树,每次用轻子树的所有点在set中查询前驱后继并构建三元组,最后执行合并即可。
接下来的问题和整棵树就没啥关系了:
有 \(\mathcal O(n\log n)\) 个三元组 \((l,r,x)\) ,若 \([l,r]\subseteq\) 询问区间,则颜色 \(x\) 被激活,求被激活的不同颜色数。
本质是矩形数颜色,考虑扫描线:
扫描右端点 \(r\) ,显然每个 \(x\) 的贡献是一段前缀,维护能让 \(x\) 产生贡献的最大左端点 \(l\) ,树状数组支持单点修改后缀求和即可。
时间复杂度 \(\mathcal O(n\log^2n+m\log n)\) 。
#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int maxn=1e5+5;
int l,m,n,r,u,v,w,cnt;
ll c[maxn],d[maxn];
int id[maxn],sz[maxn],dfn[maxn],son[maxn];
int pos[maxn],res[5*maxn];
set<int> s;
vector<pii> g[maxn],h[maxn],q[maxn];
void dfs1(int u,int fa)
{
sz[u]=1;
for(auto p:g[u])
{
int v=p.fi,w=p.se;
if(v==fa) continue;
d[v]=d[u]+w,dfs1(v,u),sz[u]+=sz[v];
if(sz[v]>=sz[son[u]]) son[u]=v;
}
}
void push(int l,int r,int x)
{
h[r].push_back(mp(l,d[x]));
}
void dfs2(int u,int fa,int op)
{
dfn[u]=++cnt,id[cnt]=u,push(u,u,u);
for(auto p:g[u])
{
int v=p.fi;
if(v!=fa&&v!=son[u]) dfs2(v,u,0);
}
if(son[u]) dfs2(son[u],u,1);
for(auto p:g[u])
{
int v=p.fi;
if(v==fa||v==son[u]) continue;
for(int i=dfn[v];i<dfn[v]+sz[v];i++)
{
int x=id[i];
auto it=s.lower_bound(x);
if(it!=s.end()) push(x,*it,u);
if(it!=s.begin()) push(*--it,x,u);
}
for(int i=dfn[v];i<dfn[v]+sz[v];i++) s.insert(id[i]);
}
if(!op) s.clear();
else s.insert(u);
}
void add(int x,int v)
{
while(x) c[x]+=v,x-=x&(-x);
}
int query(int x)
{
int res=0;
while(x<=n) res+=c[x],x+=x&(-x);
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n-1;i++)
{
scanf("%d%d%d",&u,&v,&w);
g[u].push_back(mp(v,w)),g[v].push_back(mp(u,w));
}
dfs1(1,0);
for(int i=1;i<=n;i++) c[i]=d[i];
sort(c+1,c+n+1);
int k=unique(c+1,c+n+1)-c-1;
for(int i=1;i<=n;i++) d[i]=lower_bound(c+1,c+k+1,d[i])-c;
memset(c,0,sizeof(c));
dfs2(1,0,1);
for(int i=1;i<=m;i++) scanf("%d%d",&l,&r),q[r].push_back(mp(l,i));
for(int i=1;i<=n;i++)
{
for(auto p:h[i]) if(p.fi>=pos[p.se]) add(pos[p.se],-1),add(p.fi,1),pos[p.se]=p.fi;
for(auto p:q[i]) res[p.se]=query(p.fi);
}
for(int i=1;i<=m;i++) printf("%d\n",res[i]);
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17266728.html
浙公网安备 33010602011771号