历史 history

题目描述
历史学家小A正在研究一个奇怪的王国的历史。当前阶段的任务是研究该国的交通。
根据这个奇怪的王国的史书记载,史书开始记载前这个王国有 n 个城市(城市从 0 开
始标号) ,但所有城市之间都没有道路相连。
每一年,在位的国王会修建一条 x 到 y 的双向道路,一条道路可能被修建多次,但不会
修建起点和终点为同一个城市的道路。
而在这之间,国王会计划进行若干次旅行。对于计划进行的一次旅行 st->ed,如果当
时能完成这次旅行,而 t 年前不能完成这次旅行,那么国王会对之前的建设成果感到满意,
否则他会很生气,并在下一次计划旅行前都让史官记录下错误的修建道路的信息,即把 x、
y 记作(x+n-c) mod n,(y+n-c) mod n。
当然在这些年中也发生了若干次国王的交替,初始国王的 c 值为 0,而每个国王的 c 值
不一定相同,但在国王在位期间 c 值不会改变,新上位的国王开始处于不生气的状态。
请根据史书帮助小 A 得出国王每次对于计划旅行是否满意,从而辅助小 A 能够研究该
国的交通信息。

输入格式
第一行为两个整数 n,m,表示初始城市数和历史书记载的内容数。
接下来 m 行,每行是以下三种格式之一:
1 . K v :表示国王交替,新国王的 c 值为 v
2 . R x y:表示史书上记载的是国王修建了 x 到 y 的双向道路,但注意这个记录的可
能不是实际状况。
3 . T st ed t: 表示国王计划进行的一次 st->ed 的旅行, 且比较的是 t 年前的情况 (国
王可能会和史书开始记载以前的情况比较) ,注意这个记录的肯定是实际情况。
注意只有遇到 R 操作才会使年份的计数+1。

输出格式
输对于每个 T 的记录输出一行, 如果此次计划旅行令国王满意, 则输出 Y, 否则输出 X。

样例输入
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

数据范围与约定
对于 30%的数据,保证 n<=1000 ,m<=3000。
另 30%的数据满足没有发生国王的交替。
对于 100%的数据,保证 n,m<=300000,0<=v,x,y,st,ed < n,0<=t< m。
数据有梯度

思路

用并查集判断两个点是否连通,但要加一个时间限制,意味是在某事与其父节点联通的。
为了避免超时,我们需要按秩合并(也就是按层数),将规模小的并查集合并到规模大的并查集上。
此题需要注意一个问题,城市的序号是从0开始的,在样例中可以看出来。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <map>
#include <set>
#include <deque>
#include <vector>
#include <queue>
#define N 300009

int re[N];
int tim;
int size[N],ime[N];
int n,m;
int x,y,z;
char s;
int c;
bool angry;


inline void write(int x){
    if(x==0){putchar(48);return;}
    int len=0,dg[20];
    while(x>0){dg[++len]=x%10;x/=10;}
    for(int i=len;i>=1;i--)putchar(dg[i]+48);
}

inline void read(int &x){
    x=0;char c=getchar();
    while(c<'0' || c>'9')c=getchar();
    while(c>='0' && c<='9'){
        x=x*10+c-'0';
        c=getchar();
    } 
}

inline int find(int a,int t)
 {
    while(re[a]!=a&&ime[a]<=t)a=re[a];
    return a;
 }
using namespace std;
int main()
 {
    read(n);
    read(m);
    for(int i=1;i<n;i++)
     re[i]=i,size[i]=1;
    for(int i=1;i<=m;i++)
     {
        scanf("%s",&s);
        switch(s)
         {
            case'K':read(c);
                     angry=0;
                     continue;
            case'R':read(x);
                    read(y);
                    if(angry)
                     {
                        x+=(c%n);x%=n;
                        y+=(c%n);y%=n;
                              }       
                     tim++;
             x=find(x,tim);
             y=find(y,tim);
             if(x==y)continue;
             if(size[x]>size[y])
              {
                size[x]+=size[y];
                re[y]=x;
                ime[y]=tim;
                       }    
                else
                 {
                    size[y]+=size[x];
                    re[x]=y;
                    ime[x]=tim;
                             }     
                continue;
             case'T':read(x);read(y);read(z);
             angry=((find(x,tim-z)==find(y,tim-z))||find(x,tim)!=find(y,tim));
             if(angry)printf("N\n");
              else printf("Y\n");
              continue;                  

           }
      }
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值