zoukankan      html  css  js  c++  java
  • 题解「IOI 2018 狼人」

    一句话题意:(n) 个点 (m) 条边的图,有 (Q) 个询问,每次从 (s_i) 出发,只能够到达 (geq l_i) 的点,在 ([l_i,r_i]) 必须变身一次,之后只能够到达 (leq r_i) 的点,问能否到达 (t_i)

    我们发现这个问题等价于从 (s_i) 出发能够到达的点与从 (t_i) 出发能够到达的点是否存在交集。对于这一类对路径途径的点 (/) 边进行限制,却只要求判断连通性 (/) 可达性的问题,可以使用 (mathrm{Kruskal}) 重构树解决。

    基于 (mathrm{Kruskal}) 重构树的连通性与原生成树相同,可以很好的处理这个问题。但是 (mathrm{Kruskal}) 重构树需要有边权,原图上没有,怎么办呢?我们给每条边 ((x,y)) 赋上一个权值 (w) 即可。

    这里以 (leq r_i) 为例,重构过程中,把一条边转换成一个点,说明这两个点在生成树上必须经过这条边 ((u,v)),并且经过这条边时,只有 (max(u,v)) 可能会对当前经过点的最大值产生影响。所以将 ((u,v)) 的边权赋为 (max(u,v)),然后求最小生成树的 (mathrm{Kruskal}) 重构树,记这棵 (mathrm{Kruskal}) 重构树为 (T_1)(geq l_i) 的部分同理。参照上述分析过程,将 ((u,v)) 边权赋为 (min(u,v)) 再求最大生成树的 (mathrm{Kruskal}) 重构树,记这棵 (mathrm{Kruskal}) 重构树为 (T_2)

    那么,我们现在得到了两棵重构树 (T_1,T_2),并且能够在每次询问时取出这两棵树的子树 (x,y),问题变为判断这两棵子树 (x,y) 是否存在交集。将这两棵树的 (mathrm{dfs}) 序求出来。问题再次被转化成判断两个序列是否存在交集。

    记子树 (x)(T_1)(mathrm{dfs}) 序上对应区间 ([l_1,r_1]),子树 (y)(T_2)(mathrm{dfs}) 序上对应区间 ([l_2,r_2])(T_1)(mathrm{dfs}) 序列为 (a)(T_2)(mathrm{dfs}) 序列为 (b) 。将这个问题形式化,即判断 (exists iin [l_1,r_1],jin [l_2,r_2],a_i,b_jleq n,a_i=b_j)。似乎不是很好做,不妨固定其中一个约束。记 (c_x) 为节点编号 (x) 在序列 (a) 中出现的位置。则以上判定可以被写作 (exists b_jleq n,jin [l_2,r_2],c_{b_j}in [l_1,r_1])。直接主席树维护就好了。

    时间复杂度为 (O((Q+n) log n+mlog m))

    #include<cstdio>
    #include<algorithm>
    struct edge {int x,y,w;} e[2][400005];
    int n,m,t,tot[2],cnt[2],num[2];
    int h[2][400005],to[2][400005],ver[2][400005];
    int f[2][400005][20],g[2][400005][20],fa[400005];
    int dfn[2][400005],rev[2][400005],size[2][400005],val[2][400005];
    int rt[400005],sonL[16000005],sonR[16000005],sum[16000005];
    inline int read() {
    	register int x=0,f=1;register char s=getchar();
    	while(s>'9'||s<'0') {if(s=='-') f=-1;s=getchar();}
    	while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
    	return x*f;
    }
    inline int find(int x) {return x==fa[x]? x:fa[x]=find(fa[x]);}
    inline bool cmp(const edge &x,const edge &y) {return x.w<y.w;} 
    inline int max(const int &x,const int &y) {return x>y? x:y;}
    inline int min(const int &x,const int &y) {return x<y? x:y;}
    inline void assign(int x,int y) {sonL[x]=sonL[y]; sonR[x]=sonR[y]; sum[x]=sum[y];}
    inline void add(int x,int y,int id) {to[id][++cnt[id]]=y;ver[id][cnt[id]]=h[id][x];h[id][x]=cnt[id];}
    inline void dfs1(int x,const int &id,int fa) {
    	dfn[id][x]=++num[id]; rev[id][num[id]]=x; size[id][x]=1; //printf("%d
    ",x);
    	for(register int i=1;i<=19;++i) f[id][x][i]=f[id][f[id][x][i-1]][i-1],g[id][x][i]=max(g[id][x][i-1],g[id][f[id][x][i-1]][i-1]);
    	for(register int i=h[id][x],y;i;i=ver[id][i]) {if((y=to[id][i])==fa) continue; f[id][y][0]=x; g[id][y][0]=val[id][x]; dfs1(y,id,x); size[id][x]+=size[id][y];} 
    }
    inline int jump(int u,int lim,int id) {for(register int i=19;i>=0;--i) if(f[id][u][i]&&g[id][u][i]<=lim) u=f[id][u][i]; return u;}
    inline void prework(int id) {
    	std::sort(e[id]+1,e[id]+1+m,cmp); tot[id]=n; for(register int i=1;i<=2*n-1;++i) fa[i]=i;
    	for(register int i=1,fx,fy;i<=m;++i) if((fx=find(e[id][i].x))!=(fy=find(e[id][i].y))) {fa[fx]=fa[fy]=++tot[id]; val[id][tot[id]]=e[id][i].w; add(tot[id],fx,id); add(tot[id],fy,id);}
    	for(register int i=n+1;i<=tot[id];++i) if(fa[i]==i) dfs1(i,id,-1);
    }
    inline void change(int &p,int lst,int l,int r,int x) {assign(p=++t,lst); ++sum[p]; if(l==r) return; int mid=l+r>>1; x<=mid? change(sonL[p],sonL[lst],l,mid,x):change(sonR[p],sonR[lst],mid+1,r,x);}
    inline int ask(int x,int y,int l,int r,int L,int R) {if(L<=l&&r<=R) return sum[y]-sum[x]; int mid=l+r>>1,res=0; if(L<=mid) res+=ask(sonL[x],sonL[y],l,mid,L,R); if(R>mid) res+=ask(sonR[x],sonR[y],mid+1,r,L,R); return res;}
    int main() {
    	n=read();m=read(); int Q=read();
    	for(register int i=1;i<=m;++i) {
    		e[0][i].x=e[1][i].x=read()+1;
    		e[0][i].y=e[1][i].y=read()+1;
    		e[0][i].w=max(e[0][i].x,e[0][i].y);
    		e[1][i].w=-min(e[0][i].x,e[0][i].y);
    	}
    	prework(0); prework(1);
    	for(register int i=1,x;i<=tot[1];++i) {if((x=rev[1][i])<=n) change(rt[i],rt[i-1],1,tot[0],dfn[0][x]); else rt[i]=rt[i-1];}
    	while(Q--) {
    		int S=read()+1,E=read()+1,L=read()+1,R=read()+1;
    		int u=jump(S,-L,1),v=jump(E,R,0);
    		int l1=dfn[0][v],r1=dfn[0][v]+size[0][v]-1,l2=dfn[1][u],r2=dfn[1][u]+size[1][u]-1;
    		int res=ask(rt[l2-1],rt[r2],1,tot[0],l1,r1);
    		printf("%d
    ",res>0); 
    	}
    	return 0; 
    } 
    
  • 相关阅读:
    利用百度地图API实现百度地图坐标拾取
    newtonsoft.json的JObject里的JSON数据 动态
    监听微信返回按钮
    C# 如何理解如下泛型约束 class A<T>:where T:class{}
    微博数据库设计 _转
    新浪微博,腾讯微博mysql数据库主表猜想 __转
    Ferris教程学习笔记:js示例3.9 倒计时时钟
    Ferris教程学习笔记:js示例3.8 简易网页时钟
    Ferris教程学习笔记:js示例3.6 判断数字是否为两位数
    Ferris教程学习笔记:js示例3.5 页面加载后累加,自加1
  • 原文地址:https://www.cnblogs.com/tommy0103/p/13831833.html
Copyright © 2011-2022 走看看