P5311 Ynoi2011 成都七中
P5311 Ynoi2011 成都七中
点分树好题,太妙了。
思路
看到树和连通块,考虑点分树。
但是从这里发现原树和点分树的联系实在太小,貌似不可做。
可以发现对于一个询问,一个点如果和
可以证明,对于一个连通块,肯定存在一个在
证明如下:
证明一:在原树中删除点分树上的一个点,会把原树分为若干个连通块,在构建点分树的过程中
证明二:如果点分树上一个节点
想要找到点
每一个询问都对应了一个关键点,我们只需要查找点分树上关键点所在子树内,原树上到关键点最大最小节点编号在
如果每个询问做一次,效率甚至不如暴力,但我们可以把多个询问合在同一个点上做。
对于一个点分树上的点
把询问和点混在一起按照第一维降序排序,然后每一种颜色记录最小的
这里先加入树状数组的点满足了
时间复杂度:
树分治的子树内节点排序:
树分治加树状数组:
总时间复杂度:
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]);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?