【NOI模拟赛】区域划分(结论,构造)

题面

在这里插入图片描述
在这里插入图片描述
1s , 512mb

题解

首先,如果有相邻两城党派相同一定无解。

我们发现,最终的方案一定得有相邻的三座城市是在一个三角形里的,而把这个三角形连上后,相当于把中间的城市去掉,剩下的部分就又是一个子问题。

于是,我们统计相邻的能组成三角形的三元组个数 X X X(若某位置 i i i 满足 a i a_i ai 和其左右两个位置刚好构成集合 { 0 , 1 , 2 } \{0,1,2\} {0,1,2} ,则可构成三元组),不难发现,当我们连上个三角形时, X X X 减少量为奇数,且存在方案使其大于零。而最终一定是剩三个城,刚好 X X X 变为 1 的,所以——答案有解当且仅当 X > 0 X>0 X>0 X X X n n n 的奇偶性相同

如何使得 X X X 恒大于零呢?其实只需要尽量使用边缘的三元组——当且仅当某个三元组与左右两个三元组的中心城市相邻时,该三元组不是边缘三元组。若没有边缘的三元组,当然随便选一个连都没问题。因为我们发现,若选了一个非边缘三元组,会使得 X X X 减少 3 3 3 ,这时只有全都是非边缘三元组的时候能保证还存在三元组。

于是我们用 set 和链表维护三元组,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)


看了题解后,我发现我是个XX。

首先,若无相邻相同城市,则 X X X 的奇偶性一定与 n n n 相同。也就是说只要 X > 0 X>0 X>0 就有解了。而且不难证明,只要同时存在 0 , 1 , 2 0,1,2 0,1,2 X X X 就大于 0 。

然后,有很简单的构造法:

在这里插入图片描述

CODE

打着我的小笨码,逃避着我的小笨责任 😋

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#pragma GCC optimize(2)
using namespace std;
#define MAXN 100005
#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>
#define UIN unsigned int
#define eps 1e-6
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()
inline 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);}
inline void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
inline void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
int a[MAXN];
int l[MAXN],r[MAXN];
set<int> st,sp;
bool che(int x) {
	if(a[l[x]]^a[x]^a[r[x]]) return st.erase(x),0;
	return st.insert(x),1;
}
bool che2(int x) {
	if(!(a[l[x]]^a[x]^a[r[x]]) && ((a[l[l[x]]]^a[l[x]]^a[x]) || (a[x]^a[r[x]]^a[r[r[x]]]))) return sp.insert(x),1;
	return sp.erase(x),0;
}
void del(int x) {
	st.erase(x); sp.erase(x);
	int s = l[x],o = r[x];
	AIput(min(s,o)+1,' '); AIput(max(s,o)+1,'\n');
	r[s] = o; l[o] = s;
	che(s); che(o); che2(s); che2(o);
	che2(l[s]); che2(r[o]);
}
int main() {
	freopen("divide.in","r",stdin);
	freopen("divide.out","w",stdout);
	n = read();
	for(int i = 0;i < n;i ++) a[i] = read()+1;
	for(int i = 0;i < n;i ++) {
		if(a[i] == a[(i+1)%n]) return printf("No\n"),0;
		l[i] = (i+n-1)%n; r[i] = (i+1)%n;
	}
	for(int i = 0;i < n;i ++) che(i),che2(i);
	if((st.size()&1) == (n&1) && !st.empty()) {
		printf("Yes\n");
		for(int i = 1;i <= n-3;i ++) {
			int t = *st.begin();
			while(!sp.empty() && !che2(*sp.begin()));
			if(!sp.empty()) t = *sp.begin();
			del(t); while(!st.empty() && !che(*st.begin()));
		}
	}
	else printf("No");
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NOI(全国青少年信息学奥林匹克竞模拟的测试数据是指用于评测参选手的程序的输入和对应的输出。测试数据是非常重要的,因为它决定了参选手的程序能否正确地解决问题。 在NOI模拟中,测试数据具有以下特点: 1.充分覆盖:测试数据应涵盖各种可能的输入情况,包括边界条件和极端情况。通过提供不同的测试数据,可以考察选手对问题的全面理解和解决能力。 2.随机性和均衡性:为了公平起见,测试数据应该是随机生成的,而不是针对某个特定算法或解法设计的。同时,测试数据应该是均衡的,即各种情况的概率应该大致相等,以避免偏向某些解法。 3.合理性和可行性:测试数据应该是合理和可行的,即符合题目要求的输入数据,并且是选手能够通过编写程序来处理的。测试数据应该考虑到程序的限制和时间复杂度,以充分测试选手的编程能力。 NOI模拟的测试数据通常由经验丰富的考题组负责生成。他们会根据题目的要求和限制,设计出一组合理、充分、随机和均衡的测试数据,以确保参选手的程序在各种情况下都能正确运行,并且能通过性能测试。 总之,测试数据在NOI模拟中起到了至关重要的作用,它既考察了选手对问题的理解和解决能力,又提高了选手编程的技巧和效率。同时,合理和恰当的测试数据也是公平竞的保证,确保每个参选手有相同的机会和条件进行竞争。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值