P4899 [IOI2018] werewolf 狼人 题解

因为我记忆力不好,经常遇到之前做过的题一下子想不起来,所以打算基本上给每个比较有意思的题写题解,同时造福后代。
这是werewolf,它是furry,很可爱
题意:一张无向图,点有点权,每次询问从 uv 的路径中,是否存在一条先走点权大于等于 L,再走点权小于等于 R 的路径。
思路:从起点找到所有可以到达的点权大于等于 L 的节点,为集合 A,然后从终点找到所有可以到达的点权小于等于 R 的点,为集合 B。如果 AB,则存在这样的路径,否则不存在。
考虑 kruskal 重构树,关于 kruskal 重构树
构建一个小顶堆(人走),然后从起点往上跳到最后一个权值大于等于 L 的节点,此时这个点的子树中就全是权值小于等于 L 的点。大顶堆(furry走)同理。
两个堆分别建一个 dfn 序列 ab,此时就相当于询问序列 alara 范围内的元素有没有在序列 blbrb 的范围内出现。(二维数点),直接上离线树状数组或者主席树即可。
代码

#include<bits/stdc++.h>
inline int read(){
char ch=getchar();int x=0,f=1;
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
const int N=4e5+10;
int n,m,q;
struct Edge{int u,v,w;}ed[N];
inline bool cmp_per(Edge a,Edge b){return a.w>b.w;}
inline bool cmp_wolf(Edge a,Edge b){return a.w<b.w;}
struct Ktree{
int dfn[N],fa[N],st[25][N],head[N],val[N],dfn_cnt=0,e_cnt=0,lf[N],rf[N];
struct edge{int v,nex;}e[N];
inline void add(int u,int v){e[++e_cnt]={v,head[u]};head[u]=e_cnt;}
inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
inline void dfs(int x){
lf[x]=++dfn_cnt;dfn[dfn_cnt]=x;
for(int i=1;i<=20;++i)st[i][x]=st[i-1][st[i-1][x]];
for(int i=head[x];i;i=e[i].nex)dfs(e[i].v);
rf[x]=dfn_cnt;
}
inline void build(int type){
for(int i=1;i<=2*n-1;++i)fa[i]=i;
for(int i=1;i<=m;++i)ed[i].w=type?std::min(ed[i].u,ed[i].v):std::max(ed[i].u,ed[i].v);
if(type)std::stable_sort(ed+1,ed+m+1,cmp_per);else std::stable_sort(ed+1,ed+m+1,cmp_wolf);
int node_tot=n+1;
for(int i=1;i<=m&&node_tot<=2*n-1;++i){
int u=ed[i].u,v=ed[i].v,w=ed[i].w;
int fu=find(u),fv=find(v);
if(fu!=fv){
val[node_tot]=w;
add(node_tot,fu),add(node_tot,fv);
st[0][fu]=st[0][fv]=fa[fu]=fa[fv]=node_tot;
node_tot++;
}
}
dfs(node_tot-1);
}
inline int get(int x,int v,int type){
for(int i=20;i>=0;--i){
if(type&&st[i][x]&&val[st[i][x]]>=v)x=st[i][x];
if(!type&&st[i][x]&&val[st[i][x]]<=v)x=st[i][x];
}
return x;
}
}Per,Wolf;
int root[N],cnt;
struct Tree{int siz,ls,rs;}t[N<<5];
inline void update(int p){t[p].siz=t[t[p].ls].siz+t[t[p].rs].siz;}
inline void insert(int last,int p,int x,int l,int r){
if(l==r){t[p].siz=t[last].siz+1;return;}
int mid=(l+r)>>1;t[p]=t[last];
if(x<=mid)insert(t[last].ls,t[p].ls=++cnt,x,l,mid);
else insert(t[last].rs,t[p].rs=++cnt,x,mid+1,r);
update(p);
}
inline int query(int last,int p,int x,int y,int l,int r){
if(l>=x&&r<=y){return t[p].siz-t[last].siz;}
int mid=(l+r)>>1,ans=0;
if(x<=mid)ans+=query(t[last].ls,t[p].ls,x,y,l,mid);
if(y>mid)ans+=query(t[last].rs,t[p].rs,x,y,mid+1,r);
return ans;
}
int main(){
// freopen("in.in","r",stdin),freopen("out.out","w",stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0),std::cout.tie(0);
n=read(),m=read(),q=read();
int len=2*n-1;
for(int i=1;i<=m;++i)
ed[i].u=read()+1,ed[i].v=read()+1;
Per.build(1),Wolf.build(0);
for(int i=1;i<=Wolf.dfn_cnt;++i){
root[i]=root[i-1];
int x=Wolf.dfn[i];
if(x<=n)insert(root[i-1],root[i]=++cnt,Per.lf[x],1,len);
}
for(int i=1;i<=q;++i){
int u=read(),v=read(),l=read(),r=read();
u++,v++,l++,r++;
int start=Per.get(u,l,1),end=Wolf.get(v,r,0);
if(query(root[Wolf.lf[end]-1],root[Wolf.rf[end]],Per.lf[start],Per.rf[start],1,len))
std::cout<<1<<'\n';
else std::cout<<0<<'\n';
}
}
posted @   Ishar-zdl  阅读(54)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示