zoukankan      html  css  js  c++  java
  • 【洛谷P4899】werewolf 狼人

    题目

    题目链接:https://www.luogu.com.cn/problem/P4899
    在日本的茨城县内共有 (N) 个城市和 (M) 条道路。这些城市是根据人口数量的升序排列的,依次编号为 (0)(N - 1)。每条道路连接两个不同的城市,并且可以双向通行。由这些道路,你能从任意一个城市到另外任意一个城市。

    你计划了 (Q) 个行程,这些行程分别编号为 (0)(Q - 1)。第 (i(0 leq i leq Q - 1)) 个行程是从城市 (S_i) 到城市 (E_i)

    你是一个狼人。你有两种形态:人形狼形。在每个行程开始的时候,你是人形。在每个行程结束的时候,你必须是狼形。在行程中,你必须要变身(从人形变成狼形)恰好一次,而且只能在某个城市内(包括可能是在 (S_i)(E_i) 内)变身。

    狼人的生活并不容易。当你是人形时,你必须避开人少的城市,而当你是狼形时,你必须避开人多的城市。对于每一次行程 (i(0 leq i leq Q - 1)),都有两个阈值 (L_i)(R_i(0 leq L_i leq R_i leq N - 1)),用以表示哪些城市必须要避开。准确地说,当你是人形时,你必须避开城市 (0, 1, ldots , L_i - 1) ;而当你是狼形时,则必须避开城市 (R_i + 1, R_i + 2, ldots , N - 1)。这就是说,在行程 (i) 中,你必须在城市 (L_i, L_i + 1, ldots , R_i) 中的其中一个城市内变身。

    你的任务是,对每一次行程,判定是否有可能在满足上述限制的前提下,由城市 (S_i) 走到城市 (E_i)。你的路线可以有任意长度。

    思路

    对于一组询问,我们求出在人形和狼形的时候分别可以到达的点集,然后判断一下有无交即可。
    我们设边权为连接的两个点的较小点权,求出其 kruskal 生成树,那么对于一次询问,在狼形的时候可以走的点集就是其中一个点为根的子树。同理可以设边权为较大点权,其 kruskal 生成树中的一个子树为在人形时可以走的点集。
    求出两棵 kruskal 生成树之后,对于一组询问 (s,t,l,r),我们可以通过倍增找出 (s)(t) 分别在两棵树中的一个祖先节点,满足这个祖先节点的权值在可以走的范围内且深度较小。那么这两个节点的子树就是可以走的点权。
    求出两棵树的 dfs 序,我们需要判断的就是两个 dfs 序的两个区间是否有交。我们以其中一棵树的 dfs 序建立可持久化线段树,以另一棵树的 dfs 序作为下标。询问时就直接判断第一个区间的可持久化线段树内,值域在第二个区间的和是否为 (0) 即可。
    时间复杂度 (O((m+Q)log n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=400010,LG=20;
    int n,m,Q,rt[N];
    
    struct edge1
    {
    	int u,v;
    }e1[N];
    
    bool cmp1(edge1 x,edge1 y)
    {
    	return max(x.u,x.v)<max(y.u,y.v);
    }
    
    bool cmp2(edge1 x,edge1 y)
    {
    	return min(x.u,x.v)>min(y.u,y.v);
    }
    
    struct Kruscal
    {
    	int tot,father[N],val[N],head[N],rk[N],dfn[N],size[N],dep[N],f[N][LG+1];
    	
    	struct edge2
    	{
    		int next,to;
    	}e[N];
    	
    	void add(int from,int to)
    	{
    		e[++tot]=(edge2){head[from],to};
    		head[from]=tot;
    	}
    	
    	int find(int x)
    	{
    		return x==father[x]?x:father[x]=find(father[x]);
    	}
    	
    	void kruskal(int id)
    	{
    		memset(head,-1,sizeof(head));
    		if (id==1) sort(e1+1,e1+1+m,cmp1);
    			else sort(e1+1,e1+1+m,cmp2);
    		for (int i=1;i<=n*2;i++) father[i]=i;
    		int num=n;
    		for (int i=1;i<=m;i++)
    		{
    			int x=find(e1[i].u),y=find(e1[i].v);
    			if (x!=y)
    			{
    				num++;
    				if (id==1) val[num]=max(e1[i].u,e1[i].v);
    					else val[num]=min(e1[i].u,e1[i].v);
    				add(num,x); add(num,y);
    				father[x]=father[y]=num;
    			}
    		}
    	}
    	
    	int dfs(int x,int fa)
    	{
    		dep[x]=dep[fa]+1; f[x][0]=fa;
    		rk[x]=++tot; dfn[tot]=x; size[x]=1;
    		if (!val[x]) val[x]=val[fa];
    		for (int i=1;i<=LG;i++)	
    			f[x][i]=f[f[x][i-1]][i-1];
    		for (int i=head[x];~i;i=e[i].next)
    			size[x]+=dfs(e[i].to,x);
    		return size[x];
    	}
    	
    	int binary(int x,int l,int r)
    	{
    		for (int i=LG;i>=0;i--)
    			if (val[f[x][i]]>=l && val[f[x][i]]<=r) x=f[x][i];
    		return x;
    	}
    }t1,t2;
    
    struct SegTree
    {
    	int tot,lc[N*LG*4],rc[N*LG*4],sum[N*LG*4];
    	
    	int ins(int now,int l,int r,int k)
    	{
    		int x=++tot;
    		lc[x]=lc[now]; rc[x]=rc[now]; sum[x]=sum[now]+1;
    		if (l==r) return x;
    		int mid=(l+r)>>1;
    		if (k<=mid) lc[x]=ins(lc[now],l,mid,k);
    			else rc[x]=ins(rc[now],mid+1,r,k);
    		return x;
    	}
    	
    	int query(int nowl,int nowr,int l,int r,int ql,int qr)
    	{
    		if (l==ql && r==qr)
    			return sum[nowr]-sum[nowl];
    		int mid=(l+r)>>1;
    		if (qr<=mid) return query(lc[nowl],lc[nowr],l,mid,ql,qr);
    		if (ql>mid) return query(rc[nowl],rc[nowr],mid+1,r,ql,qr);
    		return query(lc[nowl],lc[nowr],l,mid,ql,mid)+query(rc[nowl],rc[nowr],mid+1,r,mid+1,qr);
    	}
    }seg;
    
    int main()
    {
    	scanf("%d%d%d",&n,&m,&Q);
    	for (int i=1;i<=m;i++)
    	{
    		scanf("%d%d",&e1[i].u,&e1[i].v);
    		e1[i].u++; e1[i].v++;
    	}
    	t1.kruskal(1); t2.kruskal(2);
    	t1.tot=t2.tot=0;
    	for (int i=1;i<=2*n;i++)
    	{
    		if (t1.find(i)==i) t1.dfs(i,0);
    		if (t2.find(i)==i) t2.dfs(i,0);
    	}
    	for (int i=1;i<=n*2;i++)
    		if (t2.dfn[i]<=n) rt[i]=seg.ins(rt[i-1],1,n*2,t1.rk[t2.dfn[i]]);
    			else rt[i]=rt[i-1];
    	while (Q--)
    	{
    		int s,t,l,r;
    		scanf("%d%d%d%d",&s,&t,&l,&r);
    		s=t2.binary(s+1,l+1,n); t=t1.binary(t+1,1,r+1);
    		int p=seg.query(rt[t2.rk[s]-1],rt[t2.rk[s]+t2.size[s]-1],1,n*2,t1.rk[t],t1.rk[t]+t1.size[t]-1);
    		printf("%d
    ",(bool)p);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Eclipse '《》'operator is not allowed for source level below 1.7
    eclipse怎么在包里建一个包
    java jdk environment variables
    甲骨文中国 / Oracle 提供的技术资源
    在Eclipse中使用log4j配置实例听
    StringBuilder的常用方法
    mediasoup-demo公网安装部署
    使用NodeJs新建一个https服务器,并可以发布静态资源
    使用shell脚本批量执行adb命令,卸载安装apk
    这 5 个前端组件库,可以让你放弃 jQuery UI
  • 原文地址:https://www.cnblogs.com/stoorz/p/14083280.html
Copyright © 2011-2022 走看看