【NOI模拟赛】给国与时光鸡(构造)

题目背景

参见校长的博客

题面

1s,512mb

给国是个神奇的国家。由于给太祖的治理有方(且发现了给国定理),给国科技光速发展,发明出了时光鸡。给太祖有幸成为第一个食用时光鸡的人。

一开始时光鸡会跳到 0 0 0 时刻。然后往后正常走每段时间,当到达 2 n + 1 2n+1 2n+1 时被捉住。但令给太祖没有想到的是,它所经过的时间线因为虫洞的影响被打乱了。具体来说, i i i 时刻和 j j j 时刻通过一个虫洞连接。如果时光鸡从 i − 1 i-1 i1 到了 i i i 时刻,它会被立即传送到 j j j 时刻。如果时光鸡从 j − 1 j-1 j1 到了 j j j 时刻,它会被立即传送到 i i i 时刻。

总共有 n n n 对虫洞连接两个不同的时刻。每个 1 ∼ 2 n 1\sim 2n 12n 的时刻恰好被一个虫洞连接。由于 0 0 0 时刻和 2 n + 1 2n+1 2n+1 时刻没有虫洞,容易证明时光鸡总是能到达 2 n + 1 2n+1 2n+1 时刻。由于时间久远,虫洞已经消失了,只能看到哪段时间上有鸡脚印。

你需要根据给太祖给出的记录还原出一种可行的虫洞匹配方案,或者告诉给太祖记录出错了。

输入格式

第一行两个整数 n , m n,m n,m,表示总时刻为 2 n + 1 2n+1 2n+1 以及存在脚印的时间总长。

接下来一行 m m m 个数,第 i i i 个数 a i a_i ai 表示 ( a i , a i + 1 ) (a_i,a_i+1) (ai,ai+1) 这段时间有鸡爪印。

输出格式

如果无解输出 No ,否则先输出一行 Yes ,然后输出 n n n 行,每行两个数 x , y x,y x,y ,表示 ( x , y ) (x,y) (x,y) 之间有一个虫洞相连。

如果有多种构造方案,输出任意一组即可。

样例输入

3 6
0 1 2 4 5 6

样例输出

Yes
1 5
2 6
3 4

数据范围与提示

m ≤ 2 n + 1 , a i − 1 < a i , a 1 = 0 , a m = 2 n m\leq2n+1,a_{i-1}m2n+1,ai1<ai,a1=0,am=2n

0 ≤ n ≤ 100000 , 0 ≤ m ≤ 200000 0\leq n\leq100000,0\leq m\leq 200000 0n100000,0m200000

题解

我们令中间空出来的连续段个数为 A A A ,那么容易证明当 m − A − 1 m-A-1 mA1 为奇数的时候一定无解,因为你一定会将必走的区间匹配到不可走的区间上。

然后就有两种情况, ( m − A − 1 ) % 4 = 0 (m-A-1)\%4=0 (mA1)%4=0 ( m − A − 1 ) % 4 = 2 (m-A-1)\%4=2 (mA1)%4=2

第一种情况可以构造证明一定有解:

  1. 先把每个不可走的极长连续段的两端连起来,这样可走区间中还有 m − A − 1 m-A-1 mA1 个可以互相匹配。
  2. 把这些匹配点按顺序放到一个双端队列上。
  3. 每次拿出队列开头的两个元素 A < B AA<B ,队尾的两个元素 C < D CC<D
  4. 匹配 ( A , C ) , ( B , D ) (A,C),(B,D) (A,C),(B,D) ,这样就会先后经过 ( C , C + 1 ) , ( B , B + 1 ) , (C,C+1),(B,B+1), (C,C+1),(B,B+1), 队列剩余部分, ( A , A + 1 ) , ( D , D + 1 ) (A,A+1),(D,D+1) (A,A+1),(D,D+1)
  5. 跳回第 3 步,直到队列为空。
    【NOI模拟赛】给国与时光鸡(构造)_第1张图片

第二种情况我们发现也是可能有解的,当且仅当存在这么一种结构:
在这里插入图片描述
中间一段长度至少为 2 的极长可走区间,两边用不可走区间分隔,左右至少一边存在可匹配的可走区间。

以左边为例,如果左边存在一个可走区间了,就是上图中左边第一个红点,那么我们可以搭这样的结构:

【NOI模拟赛】给国与时光鸡(构造)_第2张图片
该点与中间区间的左边第一个点匹配,两段不可走区间的两端连一个大拱桥,剩下的可匹配点就有 m − A − 3 m-A-3 mA3 个了,可以当作 % 4 = 0 \%4=0 %4=0 的情况构造。将经过该结构的过程删去,剩下的部分的行走刚好是连续的,容易证明正确性。

CODE

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// #pragma GCC optimize(2)
using namespace std;
#define MAXN 200005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#define PR pair<int,int>
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
// #define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
int a[MAXN],ct;
PR b[MAXN];
bool f[MAXN],aa[MAXN];
deque<int> q;
int main() {
	freopen("shuttle.in","r",stdin);
	freopen("shuttle.out","w",stdout);
	n = read(); m = read();
	int cn = 0,flg = 0,l = 0,r = 0;
	for(int i = 1;i <= m;i ++) {
		a[i] = read();
		if(i > 1 && a[i] > a[i-1]+1) {
			cn ++; aa[a[i]] = 1;
			if(cn > 1 && a[i-1] == a[i-2]+1) flg = 1,r = i-1;
		}
	}
	if((m - cn) % 4 == 1) {
		for(int i = 2;i <= m;i ++) {
			if(a[i] > a[i-1]+1) {
				b[++ ct] = {a[i-1]+1,a[i]};
				f[a[i-1]+1] = f[a[i]] = 1;
			}
			else q.push_back(a[i]);
		}
		while(!q.empty()) {
			int A,B,C,D;
			A = q.front(); q.pop_front();
			B = q.front(); q.pop_front();
			D = q.back(); q.pop_back();
			C = q.back(); q.pop_back();
			b[++ ct] = {A,C}; b[++ ct] = {B,D};
			f[A] = f[B] = f[C] = f[D] = 1;
		}
		int p = 0;
		for(int i = 1;i <= (n<<1);i ++) {
			if(!f[i]) {
				if(p) b[++ ct] = {p,i},p = 0;
				else p = i;
			}
		}
		sort(b + 1,b + 1 + ct);
		printf("Yes\n");
		for(int i = 1;i <= n;i ++) {
			AIput(b[i].FI,' ');
			AIput(b[i].SE,'\n');
		}
		return 0;
	}
	if((m - cn) % 4 == 3 && flg) {
		l = r-1;
		while(!aa[a[l]]) l --;
		int p1,p2;
		p1 = a[l-1]+1; p2 = a[r+1];
		b[++ ct] = {p1,p2}; f[p1] = f[p2] = 1;
		p1 = a[l]; p2 = a[r]+1;
		b[++ ct] = {p1,p2}; f[p1] = f[p2] = 1;
		int ll = l-1,rr = r+2;
		while(aa[a[ll]]) ll --;
		while(aa[a[rr]] && rr <= m) rr ++;
		if(ll > 1) {
			p1 = a[ll]; p2 = a[l+1];
			b[++ ct] = {p1,p2}; f[p1] = f[p2] = 1;
		}
		else if(rr <= m) {
			p1 = a[r]; p2 = a[rr];
			b[++ ct] = {p1,p2}; f[p1] = f[p2] = 1;
		}
		else return printf("No\n"),0;
		for(int i = 2;i <= m;i ++) {
			if(a[i] > a[i-1]+1) {
				if(i != l && i != r+1) {
					b[++ ct] = {a[i-1]+1,a[i]};
					f[a[i-1]+1] = f[a[i]] = 1;
				}
			}
			else if(!f[a[i]]) {
				q.push_back(a[i]);
			}
		}
		while(!q.empty()) {
			int A,B,C,D;
			A = q.front(); q.pop_front();
			B = q.front(); q.pop_front();
			D = q.back(); q.pop_back();
			C = q.back(); q.pop_back();
			b[++ ct] = {A,C}; b[++ ct] = {B,D};
			f[A] = f[B] = f[C] = f[D] = 1;
		}
		int p = 0;
		for(int i = 1;i <= (n<<1);i ++) {
			if(!f[i]) {
				if(p) b[++ ct] = {p,i},p = 0;
				else p = i;
			}
		}
		sort(b + 1,b + 1 + ct);
		printf("Yes\n");
		for(int i = 1;i <= n;i ++) {
			AIput(b[i].FI,' ');
			AIput(b[i].SE,'\n');
		}
		return 0;
	}
	printf("No\n");
	return 0;
}

你可能感兴趣的:(交互题\构造(乱搞)题,算法,c++,构造,模拟)