【题解】P4899 [IOI2018] werewolf 狼人

题目描述

在日本的茨城县内共有 N 个城市和 M 条道路。这些城市是根据人口数量的升序排列的,依次编号为 0N1。每条道路连接两个不同的城市,并且可以双向通行。由这些道路,你能从任意一个城市到另外任意一个城市。

你计划了 Q 个行程,这些行程分别编号为 0Q1。第 i(0iQ1) 个行程是从城市 Si 到城市 Ei

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

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

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

题解

看到走边大小的限制就想到kuXXX重构树,建出重构树怎么判联通呢?
注意到一个点对应了两棵树上的单点信各一个区间,于是想到扫描线,一个点分别的dfs序构成二维坐标,而查询则是询问矩形内是否有点。
总体来说挺板的这题怎么评黑啊

#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}
const int N=200010;
int n,m,q;
struct Tree{
	int tag;
	int Fa[N*2],fa[N*2][21],dfn[N*2],siz[N*2],cnt,tot;
	int sum[N*2],dep[N*2];
	int head[N*2],to[N*4],fro[N*4],tail;
	inline void adlin(int x,int y){
		to[++tail]=y,fro[tail]=head[x],head[x]=tail;
		if(tag==1)sum[0]=0;
		else sum[0]=INT_MAX;
		return ;
	}
	inline void init(int t){
		tag=t;
		for(int i=1;i<=n*2;i++)Fa[i]=i;
		cnt=n;
		return ;
	}
	inline int getfa(int x){
		return (Fa[x]==x)?x:Fa[x]=getfa(Fa[x]);
	}
	void dfs(int u){
		dfn[u]=++tot,siz[u]=1,dep[u]=dep[fa[u][0]]+1;
		for(int k=1;(1<<k)<dep[u];k++)fa[u][k]=fa[fa[u][k-1]][k-1];
		for(int k=head[u];k;k=fro[k]){
			int x=to[k];
			fa[x][0]=u;
			dfs(x);
			siz[u]+=siz[x];
		}
		return ;
	}
	void merge(int x,int y,int z){
		x=getfa(x),y=getfa(y);
		if(x==y)return ;
		cnt++,Fa[x]=cnt,Fa[y]=cnt;
		adlin(cnt,x),adlin(cnt,y);
		sum[cnt]=z;
		return ;
	}
	void bug_dfs(int u){
//		cout<<u<<" "<<fa[u][0]<<" "<<sum[u]<<"\n";
//		cout<<u<<":"<<sum[u]<<"-"<<fa[u][0]<<"\n";
		for(int k=head[u];k;k=fro[k]){
			int x=to[k];
			bug_dfs(x);
		}
		return ;
	}
	void bug(){
		int rt=getfa(1);
		bug_dfs(rt);
		return;
	}
	pair<int,int> get(int u,int x){
		for(int k=20;k>=0;k--){
			if(tag==1&&sum[fa[u][k]]>=x)u=fa[u][k];
			if(tag==2&&sum[fa[u][k]]<=x)u=fa[u][k];
		}
//		cout<<"get:"<<tag<<"-"<<u<<"\n";
		return make_pair(dfn[u],dfn[u]+siz[u]-1);
	}
}ma,mi;
struct Line{
	int x,y,v;
}lin[N*2];
int tr[N];
inline int lowbit(int x){return x&-x;}
inline void add(int x,int y){
	if(!x)return ;
	while(x<=n*2)tr[x]+=y,x+=lowbit(x);
	return ;
}
inline int get(int x){
	int ansn=0;
	while(x)ansn+=tr[x],x-=lowbit(x);
	return ansn;
}
int ans[N];
struct Que{
	int t,p,l,r,id;
}que[N*10];
int cnt;
signed main(){
	n=rd(),m=rd(),q=rd();
	ma.init(1),mi.init(2);
	for(int i=1;i<=m;i++)lin[i].x=rd()+1,lin[i].y=rd()+1,lin[i].v=min(lin[i].x,lin[i].y);
	sort(lin+1,lin+1+m,[&](Line a,Line b){return a.v>b.v;});
	for(int i=1;i<=m;i++)ma.merge(lin[i].x,lin[i].y,lin[i].v);
	for(int i=1;i<=m;i++)lin[i].v=max(lin[i].x,lin[i].y);
	sort(lin+1,lin+1+m,[&](Line a,Line b){return a.v<b.v;});
	for(int i=1;i<=m;i++)mi.merge(lin[i].x,lin[i].y,lin[i].v);
	int rt=ma.getfa(1);
	ma.dfs(rt);
	rt=mi.getfa(1),mi.dfs(rt);
	
	mi.bug();
//	cout<<"kkk\n";
	for(int i=1;i<=q;i++){
		int s=rd()+1,t=rd()+1,l=rd()+1,r=rd()+1;
		pair<int,int>a=ma.get(s,l);
		pair<int,int>b=mi.get(t,r);
//		cout<<s<<" "<<l<<":"<<a.first<<" "<<a.second<<"\n";
		que[++cnt]=(Que){-1,a.first-1,b.first,b.second,i};
		que[++cnt]=(Que){1,a.second,b.first,b.second,i};
	}
	for(int i=1;i<=n;i++)que[++cnt]=(Que){0,ma.dfn[i],mi.dfn[i],mi.dfn[i],0};
	sort(que+1,que+1+cnt,[&](Que a,Que b){
		if(a.p!=b.p)return a.p<b.p;
		return abs(a.t)<abs(b.t); 
	});
	for(int i=1;i<=cnt;i++){
		if(!que[i].t)add(que[i].l,1);
		else{
			ans[que[i].id]+=(get(que[i].r)-get(que[i].l-1))*que[i].t;
		}
	}
	for(int i=1;i<=q;i++)printf("%d\n",ans[i]!=0);
	return 0;
}
/*
7 8
2 6
9 1
3 10
10 5
3 8
9 6
7 1
4 5

9 10 4
*/
posted @   flywatre  阅读(35)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示