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\) 行,每行是以下三种格式之一:
-
K c
,表示国王交替,以及新国王的 \(c\) 值(含义如题目所述)。 -
R x y
,表示国王计划在 \(x,y\) 之间修建一条双向道路,但可能会因为国王生气,并不会在 \(x,y\) 之间修建道路,具体见题目描述。 -
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\),所以要还原。
本文来自博客园,作者:Aurora_Borealis,转载请注明原文链接:https://www.cnblogs.com/Aurora-Borealis-Not-Found/p/17152936.html