[IOI2018]狼人

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[IOI2018]狼人相关的知识,希望对你有一定的参考价值。

狼人

题解

每个询问要求从我们前半段只能走大于等于 L L L的边,后半段只能走小于等于 R R R的边。
也就是说,我们前半段经过的节点都是从 S S S出发,能到的大于等于 L L L的节点,而后半段,则是从 T T T出发,能到达的小于等于 R R R的节点。
这很明显是一个 K r u s k a l Kruskal Kruskal重构树的形式,我们将大于等于 L L L的边全部加进去,此时 L L L所在的联通块就是它能到达的点。
我们像重构树一样,每次加入边时就建新点连接两个端点所在子树的根,建成一棵新的树。
由于重构树上从叶子到根的链上,节点的点权是单调不升的,我们每次查询就只需要通过倍增找到最浅的点权大于 L L L的点即可,这个点的子树内的点,从 S S S出发都可以到达。
T T T出发可到达的节点也可以通过 K r u s k a l Kruskal Kruskal重构树方法找到。

由于我们需要一个节点变身,所以我们肯定需要一个两个点出发都能走到的地方。
也就是对于两棵子树,求他们的交集。
显然,子树求交,这是一个经典的区间数点问题。
在子树内的点的 d f s dfs dfs序是连续的,我们可以将两棵树的 d f s dfs dfs序列找出来,原问题就变成了查询这两个 d f s dfs dfs序列的区间中有没有共同点。
我们可以用可持久化线段树来维护区间的点,每次将当前点在另一个序列中的 d f s dfs dfs序加入线段树,那么我们每次查询只需要差分,看着一段区间内有没有另一个区间中的点即可。

时间复杂度 O ( ( n + m + q ) log ⁡   n ) O\\left((n+m+q)\\log\\,n\\right) O((n+m+q)logn)

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 400005
#define lowbit(x) (x&-x)
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;     
const LL INF=0x3f3f3f3f3f3f3f3f;  
const int mo=998244353;
const int inv2=499122177;
const int jzm=2333;
const int n1=400;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-10;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){putchar('\\n');while(x>9){putchar((x%10)|'0');x/=10;}putchar(x|'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,m,q,fa[MAXN],head[MAXN],tot,idx,idd,root[MAXN];
int dfn1[MAXN],rd1[MAXN],dfn2[MAXN],rd2[MAXN];
int val2[MAXN],f2[MAXN][20],val1[MAXN],f1[MAXN][20]; 
struct ming{int u,v;}s[MAXN];
struct edge{int to,nxt;}e[MAXN];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i;}
int findSet(int x){return fa[x]==x?x:fa[x]=findSet(fa[x]);}
bool cmp1(ming x,ming y){return max(x.u,x.v)<max(y.u,y.v);}
bool cmp2(ming x,ming y){return min(x.u,x.v)>min(y.u,y.v);}
struct tann{
	int lson,rson,sum;
	tann(){lson=rson=sum=0;}
};
class SegmentTree{
	private:
		tann tr[MAXN*22];int tot;
	public:
		void insert(int &now,int las,int l,int r,int ai){
			if(l>r||l>ai||r<ai)return ;
			tr[now=++tot]=tr[las];tr[now].sum++;
			if(l==r)return ;int mid=l+r>>1;
			if(ai<=mid)insert(tr[now].lson,tr[las].lson,l,mid,ai);
			if(ai>mid)insert(tr[now].rson,tr[las].rson,mid+1,r,ai);
		}
		int query(int rt,int l,int r,int al,int ar){
			if(l>r||l>ar||r<al||al>ar||!rt)return 0;
			if(al<=l&&r<=ar)return tr[rt].sum;int mid=l+r>>1,res=0;
			if(al<=mid)res+=query(tr[rt].lson,l,mid,al,ar);
			if(ar>mid)res+=query(tr[rt].rson,mid+1,r,al,ar);
			return res;
		}
}T;
void dosaka1(int u,int fa){
	dfn1[u]=idd+1;if(u<=n)idd++;f1[u][0]=fa;
	for(int i=1;i<19;i++)f1[u][i]=f1[f1[u][i-1]][i-1];
	for(int i=head[u];i;i=e[i].nxt)
		dosaka1(e[i].to,u);
	rd1[u]=idd;
}
void dosaka2(int u,int fa){
	dfn2[u]=idd+1;f2[u][0]=fa;
	if(u<=n){idd++;T.insert(root[idd],root[idd-1],1,n,dfn1[u]);}
	for(int i=1;i<19;i++)f2[u][i]=f2[f2[u][i-1]][i-1];
	for(int i=head[u];i;i=e[i].nxt)
		dosaka2(e[i].to,u);
	rd2[u]=idd;
}
signed main(){
	read(n);read(m);read(q);makeSet(n);idx=n;
	for(int i=1;i<=m;i++)read(s[i].u),read(s[i].v),s[i].u++,s[i].v++;
	sort(s+1,s+m+1,cmp1);val1[0]=n+1;
	for(int i=1;i<=n;i++)val1[i]=val2[i]=i;
	for(int i=1;i<=m;i++){
		int u=s[i].u,v=s[i].v,w=max(u,v);
		if((u=findSet(u))==(v=findSet(v)))continue;
		idx++;fa[idx]=fa[u]=fa[v]=idx;val1[idx]=w;
		addEdge(idx,u);addEdge(idx,v);
	}
	for(int i=idx;i>0;i--)if(!dfn1[i])dosaka1(i,0); 
	for(int i=1;i<=idx;i++)head[i]=0;tot=0;makeSet(n);idx=n;
	sort(s+1,s+m+1,cmp2);idd=0;
	for(int i=1;i<=m;i++){
		int u=s[i].u,v=s[i].v,w=min(u,v);
		if((u=findSet(u))==(v=findSet(v)))continue;
		idx++;fa[idx]=fa[u]=fa[v]=idx;val2[idx]=w;
		addEdge(idx,u);addEdge(idx,v);
	}
	for(int i=idx;i>0;i--)if(!dfn2[i])dosaka2(i,0);
	for(int i=1;i<=q;i++){
		int s,t,l,r;read(s);read(t);read(l);read(r);s++;t++;l2018 女生赛  F - 赛题分析

[IOI2018] werewolf 狼人

「IOI2018」Highway 高速公路收费

[IOI2018]组合动作——构造

[IOI2018]组合动作

[IOI 2018] Werewolf