P5311 Ynoi2011 成都七中
点分树好题,太妙了。
思路
看到树和连通块,考虑点分树。
但是从这里发现原树和点分树的联系实在太小,貌似不可做。
可以发现对于一个询问,一个点如果和 \(x\) 在一个连通块内,那么这个点到 \(x\) 的最大最小节点编号肯定都在 \([l,r]\) 这个范围内。
可以证明,对于一个连通块,肯定存在一个在 \(x\) 的连通块内的节点 \(u\),节点 \(u\) 在点分树上最浅,且节点 \(u\) 在点分树上的子树包含了整个连通块。
证明如下:
证明一:在原树中删除点分树上的一个点,会把原树分为若干个连通块,在构建点分树的过程中 \(x\) 的连通块内,最先被选中作为点分树上的一个节点为 \(u\) ,根据构建规则,剩下的点会成为 \(u\) 的子树内的节点,满足了 \(u\) 最浅和全覆盖。
证明二:如果点分树上一个节点 \(v\) 不是在连通块内,那么 \(x\) 连通块内的所有节点肯定都在该节点的同一个儿子的子树中。因为如果在不同儿子的子树中,这样节点 \(v\) 肯定也会在 \(x\) 的连通块中,与题设不符。顺着这个儿子走,直到走到一个在连通块内的点即可。
想要找到点 \(u\) 只需要依次遍历点分树上 \(x\) 最浅的祖先,选择满足原树上到 \(x\) 的最大最小节点编号在 \([l,r]\) 内的最浅的祖先。我们将这个选择的点定义为关键点。
每一个询问都对应了一个关键点,我们只需要查找点分树上关键点所在子树内,原树上到关键点最大最小节点编号在 \([l,r]\) 内的节点即可。(原树上关键点到 \(x\) 最大最小节点编号在 \([l,r]\) 内,可以从子树内的这个点到关键点,再到 \(x\) ,原树上最大最小编号也肯定在 \([l,r]\) 内)
如果每个询问做一次,效率甚至不如暴力,但我们可以把多个询问合在同一个点上做。
对于一个点分树上的点 \(u\),我们把其子树内的点到 \(u\) 的原树上最大最小编号计为 \((mn,mx)\),关键点为 \(u\) 询问存为 \((l,r)\)。
把询问和点混在一起按照第一维降序排序,然后每一种颜色记录最小的 \(mx\),遇到一个查询直接查询有多少颜色的 \(mx\) 小于 \(r\) 即可,这里可以用树状数组维护。
这里先加入树状数组的点满足了 \(l\leq mn\),查询时又满足了 \(mx\leq r\)。
时间复杂度:
树分治的子树内节点排序:\(O(n\log^2 n)\)。
树分治加树状数组:\(O(n\log^2 n)\)。
总时间复杂度:\(n\log^2 n\)。
CODE
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
struct Edge
{
int tot;
int head[maxn];
struct edgenode{int to,nxt;}edge[maxn*2];
inline void add(int x,int y)
{
tot++;
edge[tot].to=y;
edge[tot].nxt=head[x];
head[x]=tot;
}
}T;
struct node{int x,y,c,tp;};
struct treearray
{
int tree[maxn];
inline int lowbit(int x){return x&(-x);}
inline void updata(int x,int y){for(;x<=maxn-5;x+=lowbit(x)) tree[x]+=y;}
inline int getsum(int x){int sum=0;for(;x;x-=lowbit(x)) sum+=tree[x];return sum;}
inline void clr(int x){for(;x<=maxn-5;x+=lowbit(x)) tree[x]=0;}
}S;
struct fanode{int x,y,f;};//加入祖先时,记录到祖先的最大最小节点编号
inline bool cmp(node a,node b){return a.x>b.x||(a.x==b.x&&a.tp<b.tp);}
int n,m,rt;
int c[maxn],siz[maxn],mv[maxn],ans[maxn];
int mx[maxn],mn[maxn];
vector<int>s[maxn];
vector<fanode>fa[maxn];
vector<node>t[maxn];
bool book[maxn],cut[maxn];
inline int dfs_siz(int u)
{
book[u]=true;siz[u]=1;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(!book[v]&&!cut[v]) siz[u]+=dfs_siz(v);
}
book[u]=false;return siz[u];
}
inline int dfs_rt(int u,const int tot)
{
book[u]=true;int ret=u;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(!book[v]&&!cut[v]&&siz[v]*2>=tot){ret=dfs_rt(v,tot);break;}
}
book[u]=false;return ret;
}
inline void dfs2(int u,int g,int mn,int mx)
{
mx=max(mx,u),mn=min(mn,u);
t[g].push_back({mn,mx,c[u],0});//加入节点
book[u]=true;fa[u].push_back({mn,mx,g});
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(!book[v]&&!cut[v]) dfs2(v,g,mn,mx);
}
book[u]=false;siz[u]=1;
}
inline void solve(int u,int f)//建树
{
dfs_siz(u);int g=dfs_rt(u,siz[u]);cut[g]=true;
s[f].push_back(g),fa[g].push_back({g,g,g});
t[g].push_back({g,g,c[g],0});
for(int i=T.head[g];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(!cut[v]){dfs2(v,g,g,g);solve(v,g);}
}
rt=g;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&c[i]);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
T.add(x,y),T.add(y,x);
}
solve(1,0);
for(int i=1;i<=m;i++)
{
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
for(auto j:fa[x])
if(l<=j.x&&j.y<=r){t[j.f].push_back({l,r,i,1});break;}
}
memset(mv,0x7f,sizeof(mv));
for(int i=1;i<=n;i++)
{
sort(t[i].begin(),t[i].end(),cmp);
for(auto j:t[i])
if(j.tp) ans[j.c]=S.getsum(j.y);
else if(j.y<mv[j.c]){if(mv[j.c]<1e9)S.updata(mv[j.c],-1);S.updata(mv[j.c]=j.y,1);}
for(auto j:t[i]) if(!j.tp) S.clr(mv[j.c]),mv[j.c]=1e9;
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}
标签:七中,int,Ynoi2011,mx,book,edge,siz,P5311,节点
From: https://www.cnblogs.com/binbinbjl/p/18133038