zoukankan      html  css  js  c++  java
  • 题解——history(离线并查集)

    题解——history(离线并查集)

    *今天考试很水,ssw023道题都写的正解,然而不注重细节。+1or-1写成 |1 ,连通块最大值不更新 , T3就是这道细节题 *


    题面

    Description

    历史学家小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 能够研究该
    国的交通信息。

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

    in.1
    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

    out.1
    Y
    N
    Y

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

    思路

    由 YY儒 学长手写的优质题解(ssw02只提供 法二 的代码 )
    做法一:【数据结构优化暴力】
    30%做法:由于只需要知道连通性,我们可以维护森林。有一个比较好的暴力方法是,连边时在边上记录这条边连上的时刻,询问时求两点之间最迟的边即可知道这两个点连通的时刻,暴力复杂度O(nm),能拿到30%的分数。【这个思路很重要!】
    100%做法:套上个LCT即可。但由于数据范围较大,可能需要优化常数,由于数据有梯度,具体分数根据代码的丑陋程度而定。

    做法二:【利用题目性质离线】
    30%做法:对于特殊的另外30%,可以离线。那么把询问按时间排序,再套个并查集即可,复杂度O(m),能拿到30%的分数。和一个大暴力组合就能拿到60%的分数。【由于不太方便表示,将并查集的复杂度看作常数】
    100%做法:此题只有修改强制在线,而询问不强制在线,实际上仔细观察,就可以发现仍然可以离线来做。复杂度O(m),能拿到100%的分数。

    做法三:【简洁且能够处理询问也强制在线的情况】
    30%做法:暴力存下每次的并查集。复杂度O(nm)。
    70%做法:熟悉数据结构的同学应该很容易想到这个做法。这道题显然可以用可持久化并查集维护,用按秩合并套上一个可持久化线段树维护fa数组。复杂度O(nlog2n),由于数据有梯度,这个做法本地测试时给了70%的分数。但是要是又遇到跑得十分快的评测机,那我也是没办法了。/微笑/微笑/微笑
    100%做法:实际上我们并没有基于历史来修改fa数组,而只是基于历史查询,所以并不需要使用真正的可持久化。考虑套用上做法一,在并查集的边上存下这条边建立的时间。查询时查询一下到并查集的根这条路径的最小值即可知道两个结点连通的时间。复杂度O(nlogn),可以拿到100%的分数。

    AC code: 我写的相当简洁

    #include<bits/stdc++.h>
    using namespace std ;
    const  int  MAXN = 300005 ;
    inline int read(){
    	int s=0,w=1;char g=getchar() ; while(g>'9'||g<'0'){if( g=='-')w=-1;g=getchar();} 
    	while(g>='0'&&g<='9')s=s*10+g-'0',g=getchar() ;return s*w ;
    }
    int  N , M , op[ MAXN ][ 4 ] , fa[ MAXN ] , c = 0 , now = 0 ;
    int  ask[ MAXN ][ 4 ] , tot = 0 , rev[ MAXN ] ; // x , y , t , 是否建好 ; rev反问 
    char s ;//K - 1 , R - 2 , T - 3  ->0 ;
    bool  kkk = false ;//是否发火 
    priority_queue< pair< int , int > >q ;// t , tot
    void  prepare(){
    	N = read() , M = read() ; now = 0 ;
    	for( register int i = 1 ; i <= M ; ++i ){
    		scanf("%c",&s);
    		if( s == 'K' ){//换王 
    			op[ i ][ 0 ] = 1 , op[ i ][ 1 ] = read() ;
    		}
    		else if( s == 'R' ){//修路 
    			op[ i ][ 0 ] = 2 , op[ i ][ 1 ] = read() , op[ i ][ 2 ] = read() ;now++ ;
    		}
    		else if( s == 'T' ){//旅行 
    			op[ i ][ 0 ] = 3 , op[ i ][ 1 ] = read() , op[ i ][ 2 ] = read() , op[ i ][ 3 ] = max( read() , 0 ) ;
    			ask[ ++tot ][ 0 ] = op[ i ][ 1 ] , ask[ tot ][ 1 ] = op[ i ][ 2 ] , ask[ tot ][ 2 ] = now - op[ i ][ 3 ], rev[ i ] = tot ;
    			if( ask[ tot ][ 2 ] != 0 )q.push( make_pair( -ask[ tot ][ 2 ] , tot ) ) ;
    		}
    	}
    }
    inline int  find( int x ){
        if( fa[ x ] != x )fa[ x ] = find( fa[ x ] );
        return fa[ x ] ;
    }
    bool  ask_( int x , int y ){
    	int fx = find( x ) , fy = find( y ) ;
    	if( fx == fy )return true ;
    	else return false ;
    }
    inline void  combin(int x,int y){ //x-->y
    	int fx = find( x ) , fy = find( y ) ;
    	if( fx == fy )return ;
    	else fa[ fx ] = fy ;
    }
    int main(){
    	freopen("history.in","r",stdin);
    	freopen("history.out","w",stdout);
    	prepare() ;//数据离线
    	for( register int i = 1 ; i <= N ; ++i )fa[ i ] = i ;
    	c = 0 , now = 0 ; 
    	for( register int i = 1 ; i <= M ; ++i ){
    	    int now_t , now_tot ;
    		if( op[ i ][ 0 ] == 1 )c = op[ i ][ 1 ] , kkk = false ;
    		if( op[ i ][ 0 ] == 2 ){
    			now++ ;
    			if( kkk )
    				combin( (op[ i ][ 1 ]+c)%N , (op[ i ][ 2 ]+c)%N ) ;
    				else 
    			    combin( op[ i ][ 1 ] , op[ i ][ 2 ] ) ;
    			while( !q.empty() ){
    			    now_t = -q.top().first ;
    			    if( now_t > now )break ;
    			    now_tot = q.top().second ; q.pop() ;
    			    if( ask_( ask[ now_tot ][ 0 ] ,ask[ now_tot ][ 1 ] ) )ask[ now_tot ][ 3 ] = true ;
    		    }
    		}
    		if( op[ i ][ 0 ] == 3 ){
    			if( ask_(op[ i ][ 1 ],op[ i ][ 2 ]) && ask[ rev[ i ] ][ 3 ] == false ){
    				printf("Y
    ");kkk = false ;
    			}
    			else {
    				printf("N
    ");kkk = true ;
    			}
    		}
    		
    	}
    	return 0 ;
    }
    

    如有不足,请大佬指出

  • 相关阅读:
    批处理文件入门
    批处理入门学习地址
    react资料
    React 学习参考资料链接
    Spring boot + jdbc学习笔记
    iOS-升级Https证书报错
    Java-006-循环结构和控制语句详解(while, dowhile ,for ,switch)
    Java-005-运算符详解
    Java-004-变量类型和修饰符详解
    Java-001简介和基础语法[类方法、实例方法、public class 与 class 区别](第一个Java程序)
  • 原文地址:https://www.cnblogs.com/ssw02/p/11400250.html
Copyright © 2011-2022 走看看