• [ACNOI2022]王校长的构造


    题目

    题目背景
    滴——🎵 滴——🎵 踹1叉!🎵 你真了不得🎵 黑题构造,难不住你🎵 杀出个王校长!🎵

    题目描述
    可见于校长的博客

    思路

    啥也不会,先考虑拿 m = 2 n + 1 m=2n+1 m=2n+1 部分分。手玩较小的情况,发现 n = 2 n=2 n=2 有解,并可由此得到所有 2 ∣ n 2\mid n 2n 。但 n = 1 n=1 n=1 n = 3 n=3 n=3 都无解。猜测 2 ∤ n 2\nmid n 2n 无解。赛时我就想不明白这是为什么,所以我就寄了 😓

    事实上我们注意到:记 p i    ( 1 ⩽ i ⩽ 2 n ) p_i\;(1\leqslant i\leqslant 2n) pi(1i2n) 为,走完 [ i − 1 , i ] [i{-}1,i] [i1,i] 这一段之后,接下来应当走哪一段。最初 p i = i + 1 p_i=i+1 pi=i+1,特别地,令 p 2 n = 1 p_{2n}=1 p2n=1 。那么 a , b a,b a,b 之间建立虫洞,等价于交换 p a , p b p_a,p_b pa,pb 。最后 p i p_i pi 构成一个 置换,与 1 1 1 在同一置换环中的点会被走过。

    那么 m = 2 n + 1 m=2n+1 m=2n+1 希求一个完整的偶长度置换环,然而 2 ∤ n 2\nmid n 2n 表明它是奇排列,于是寄了。

    想到置换是比较关键的。话又说回来,当你意识到 “不可能死循环” 的时候,你就应该从置换的角度去思考了啊 😢

    然后考虑 m ⩽ 8 m\leqslant 8 m8 这样的部分分。发现两侧都不被经过的点,可以任意配对,并且这样的点也只能内部匹配。

    由此,我们按照左右两侧的需要被经过情况,分出四类点 N ( o n e ) , A ( l l ) , L ( e f t ) , R ( i g h t ) N(one),A(ll),L(eft),R(ight) N(one),A(ll),L(eft),R(ight) 。那么显然的, N N N N N N 配对, L L L R R R 配对, A A A A A A 配对。那么 N N N 类点就不必再考虑了。这是 简化问题 的方式。

    于是剩下 A L R A L R A ALRALRA ALRALRA 之类的东西。如果 A A A 的数量是 4 4 4 的倍数,直接用 m = 2 n + 1 m=2n+1 m=2n+1 方案构造即可,因为此时 L R LR LR 相当于路上的小插曲。如果 A A A 的数量不是 4 4 4 的倍数,我们知道,关键矛盾在于逆序对奇偶性。那么大概需要一对 L R A A L R {\color{red}L}{\color{blue}R}AA{\color{blue}L}{\color{red}R} LRAALR,然后蓝配蓝、红配红。

    画图可见,它的效果是内部一个置换环、外部一个置换环。手玩可知,只要两个环都存在 A A A,二者就可以被拼接起来。与之相对的,如果没有这样的 pattern \text{pattern} pattern 就无解。因为这是唯一拯救这罪恶的逆序对的方法。

    当然上面只表明了有解,实际上构造解的方式很 s i m p l e \rm simple simple:只需找到 A L R A L R ALRALR ALRALR,除了 L R LR LR 按照之前所说的 pattern \text{pattern} pattern 连,再把 A A AA AA 连上就行。当然外面的 A A A 也可以在右。

    代码

    #include <cstdio>
    #include <algorithm> // Almighty XJX yyds!!
    #include <cstring> // oracle: ZXY yydBUS!!!
    #include <cctype> // Huge Egg Dog ate me!!!
    #include <cstdlib>
    using llong = long long;
    # define rep(i,a,b) for(int i=(a); i<=(b); ++i)
    # define drep(i,a,b) for(int i=(a); i>=(b); --i)
    # define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
    inline int readint(){
    	int a = 0, c = getchar(), f = 1;
    	for(; !isdigit(c); c=getchar()) if(c == '-') f = -f;
    	for(; isdigit(c); c=getchar()) a = a*10+(c^48);
    	return a*f;
    }
    
    const int MAXN = 200005;
    bool vis[MAXN];	int match[MAXN];
    
    int nxt[MAXN];
    int main(){
    	int n = readint(), m = readint(), cnta = 0;
    	while(m--) vis[readint()] = true;
    	{ // basic matching for 'N'
    		int lstn = -1;
    		for(int i=1; i<=(n<<1); ++i)
    			if(!vis[i-1] && !vis[i]){ // 'N'
    				if(!(~lstn)){ lstn = i; continue; }
    				match[i] = lstn, match[lstn] = i, lstn = -1;
    			}
    			else if(vis[i-1] && vis[i]) ++ cnta;
    		if(~lstn){ puts("No"); return 0; }
    	}
    	if((cnta&3) == 2){
    		bool flag = false;
    		rep(i,1,n<<1) if(vis[i-1] && vis[i]){ // 'A'
    			int rig = i; while(rig <= (n<<1) &&
    				vis[rig-1] && vis[rig]) ++ rig;
    			int zuo[3] = {0,0,0};
    			for(int j=i-1; j; --j)
    				if(!vis[j-1] && vis[j]) zuo[2] = j;
    				else if(vis[j-1] && !vis[j]){
    					zuo[1] = j; break; // collected
    				}
    			if(!zuo[1]){ i = rig-1; continue; }
    			for(int j=zuo[1]-1; j; --j)
    				if(vis[j-1] && vis[j]){
    					zuo[0] = j; break;
    				}
    			int you[3] = {i,i,i};
    			for(int j=i+1; j<=(n<<1); ++j)
    				if(vis[j-1] && !vis[j]) you[0] = j;
    				else if(!vis[j-1] && vis[j]){
    					you[1] = j; break;
    				}
    			if(you[1] != i)
    				for(int j=you[1]+1; j<=(n<<1); ++j)
    					if(vis[j-1] && vis[j]){
    						you[2] = j; break;
    					}
    			if(zuo[0] && you[1] != i){ // link to left
    				match[zuo[0]] = i, match[i] = zuo[0];
    				match[zuo[1]] = you[1], match[you[1]] = zuo[1];
    				match[zuo[2]] = you[0], match[you[0]] = zuo[2];
    				flag = true; break;
    			}
    			if(zuo[1] && you[2] != i){ // link to right
    				match[rig-1] = you[2], match[you[2]] = rig-1; 
    				match[zuo[1]] = you[1], match[you[1]] = zuo[1];
    				match[zuo[2]] = you[0], match[you[0]] = zuo[2];
    				flag = true; break;
    			}
    			i = rig-1; // skip the segment
    		}
    		if(!flag){ puts("No"); return 0; }
    	}
    	int lstl = -1, lsta[4] = {-1,-1,-1,-1};
    	puts("Yes"); // can be sure
    	for(int i=1|(cnta=0); i<=(n<<1); ++i){
    		if(match[i]){
    			if(match[i] < i)
    				printf("%d %d\n",match[i],i);
    			continue; // 'N' or known
    		}
    		if(vis[i-1] && vis[i]){ // 'A'
    			lsta[cnta++] = i;
    			if(cnta == 4){ // done
    				printf("%d %d\n%d %d\n",lsta[0],
    				  lsta[2],lsta[1],lsta[3]);
    				cnta = 0; // reset
    			}
    		}
    		else if(vis[i-1]) // 'L'
    			lstl = i; // assert(lstl == -1);
    		else printf("%d %d\n",lstl,i);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98

    1. 作词者 Rainybunny \textsf{Rainybunny} Rainybunny,意即 XY \text{XY} XY,叉歪。 ↩︎

  • 相关阅读:
    1542. 找出最长的超赞子字符串 哈希+状态压缩
    [Python]Selenium-自动化测试
    Android自定义控件(二) Android下聚光灯实现
    GlobalMapper渲染DEM导出背景透明
    【UniApp】-uni-app-动态计算字体大小(苹果计算器)
    超全!Python图形界面框架PyQt5使用指南!
    如何获取第三方maven依赖信息?
    【Java八股文总结】之面试题(二)
    Nginx转发丢失cookie表现形式以及解决方案
    C++ 类和对象 (上)
  • 原文地址:https://blog.csdn.net/qq_42101694/article/details/125451497