history 题解(并查集)

题面

题目描述

ljj 被 S 国数不清的漂亮小姐姐所吸引,为了搞清楚为什么 S 国有如此多的漂亮小姐姐,他决定研究S国的历史。

根据 S 国史书的记载,在史书记载前,S 国有 \(n\) 个城市(城市编号 \(0\)\(n-1\)),它们彼此之间都没有道路相连,但一个城市内是联通的。

每一年,在位的国王会在城市 \(x\)\(y\) 之间修建一条双向道路,一条道路可能被修建多次,但保证无自环。

而在这之间,国王会计划进行若干次旅行。对于计划进行的一次起点为 \(st\),终点为 \(fi\) 的旅行,如果当时能完成这次旅行。而 \(t\) 年前不能完成这次旅行,国王会对之前的建设成果感到满意,否则他会很生气,并改变接下来所有计划修建的道路,即让实际修建的道路的 \(x,y\) 更换为 \((x+c) \mod n\)\((y+c) \mod n\),生气状态持续到下一次旅行开始,旅行的起点和终点可能是同一个城市。

当然这些年中也发生了若干次国王的交替,初始国王的 \(c\) 值为 \(0\),而之后每个国王的 \(c\) 值不一定相同。一个国王在位期间,他的 \(c\) 值不会改变,刚登基的国王处于不生气的状态。

请根据史书帮助 ljj 得出国王对于每次旅行是否满意。

输入格式

本题含有多组测试数据!

对于每组数据,第一行两个整数 \(n,m\) 表示城市数和史书记载的内容数。

接下来 \(m\) 行,每行是以下三种格式之一:

  1. K c,表示国王交替,以及新国王的 \(c\) 值(含义如题目所述)。

  2. R x y,表示国王计划在 \(x,y\) 之间修建一条双向道路,但可能会因为国王生气,并不会在 \(x,y\) 之间修建道路,具体见题目描述。

  3. T st ed t,表示国王计划进行一次从 \(st\)\(ed\) 的旅行,且比较的是 \(t\) 年前的情况(国王可能会史书开始记载以前的情况比较)

输出格式

对于每个 T 操作输出一行,如果国王满意输出 Y,否则输出 N。

样例输入

3 7
R 0 1 
T 0 1 1 
K 1
R 0 1
T 0 1 1 
R 0 1 
T 0 2 1 

样例输出

Y
N
Y

说明/提示

$ 1 \leq n,m \leq 3 \times 10^5$

只有遇到 R 操作才会使实际年份加一

题解

发现题目查询的是连通性问题,首先想到并查集,但普通并查集显然无法维护时间限制。

考虑使用边带权并查集维护点之间的连通性,边权为这条边建立的时间,对于查询函数新增一个参数 \(t\),查询时如果 \(t\) 小于边权则不能通行,因为当时这条边不存在(巧妙地处理了时间的性质)。

假设当前时间为 \(tim\),对于询问,查询 \(tim-t\)\(tim\) 两个时间时的联通性,若 \(tim-t\) 时不连通而 \(tim\) 时联通,则输出 Y,否则输出 N。

由于时间这种东西性质特殊无法路径压缩,所以使用按秩合并优化时间。

code:

#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define debug cout<<"DEBUG"<<endl;
#define pb push_back
#define pii pair<int,int>
#define vi vector<int>
#define imp map<int,int>
using namespace std;
const int N=3e5+5;
int n,m,fa[N],d[N],siz[N],tim,c;
bool op;
void merge(int x,int y){
	if(siz[x]>siz[y]){
		swap(x,y);
	}
	fa[x]=y;
	d[x]=tim;
	siz[y]+=siz[x];
}
int find(int x,int t){
	f(fa[x]==x||d[x]>t) return x;
	return find(fa[x],t);
}
int main(){
	freopen("history.in","r",stdin);
	freopen("history.out","w",stdout);
	while(cin>>n>>m){
		op=0;
		c=0;
		tim=0;
		for(int i=0;i<=n;i++){
			fa[i]=i;
			siz[i]=1;
			d[i]=0;
		}
		while(m--){
			char sc[5];
			scanf("%s",sc+1);
			if(sc[1]=='K'){
				op=0;
				scanf("%d",&c);
			}
			if(sc[1]=='R'){
				tim++;
				int x,y;
				scanf("%d%d",&x,&y);
				if(op){
					x=(x+c)%n;
					y=(y+c)%n;
				}
				int fx=find(x,tim);
				int fy=find(y,tim);
				if(fx!=fy){
					merge(fx,fy);
				}
			}
			if(sc[1]=='T'){
				int x,y,z;
				scanf("%d%d%d",&x,&y,&z);
				int nx=find(x,tim);
				int ny=find(y,tim);
				int ox=find(x,tim-z);
				int oy=find(y,tim-z);
				if(ox!=oy&&nx==ny){
					op=0;
					printf("Y\n");
				}else{
					op=1;
					printf("N\n");
				}
			}
		}
	}
	return 0;
}

注意代码中一个细节:

if(op){
	x=(x+c)%n;
	y=(y+c)%n;
}

这里是因为给的是 \(x+n-c\),所以要还原。

posted @ 2023-02-24 19:42  Aurora_Borealis  阅读(26)  评论(0编辑  收藏  举报