2022NOIP A层联测16

A. 平衡(balance)

打表找规律,发现一定是 +11 交替, 因为是环,转回来的话必须是奇数,所以偶数无解

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 1000005;
int n, a[maxn];
void sol(){
	n = read();
	if((n & 1) == 0){
		printf("NO\n");
		return;
	}
	printf("YES\n");
	a[1] = 1;
	int p = 1;
	for(int i = 1; i * 4 <= n + n; ++i){
		a[++p] = i * 4;
		if(i * 4 + 1 <= n + n)a[++p] = i * 4 + 1;
	}
	for(int i = 2; i <= n + n; ++i)if(i % 4 != 0 && i % 4 != 1)a[++p] = i;
	for(int i = 1; i <= n + n; ++i)printf("%d ", a[i]);
}
int main(){
	freopen("balance.in","r",stdin);
	freopen("balance.out","w",stdout);
	int t = read();
	for(int i = 1; i <= t; ++i)sol();
	return 0;
}

B. 二择(choice)

一组 n 个匹配需要 2n 个点,那么随便找一个极大匹配,合法输出,不合法一定满足有至少 n 个没有边的点

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; bool f = 0; char c = getchar();
	while(!isdigit(c)){f = c == '-'; c = getchar();}
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return f ? -x : x;
}

const int maxn = 1500005;
int n, m;
struct edge{int u, v;}e[maxn];
bool vis[maxn];
vector<int> ans;
void sol(){
	n = read(), m = read();
	for(int i = 1; i <= m; ++i){
		int u = read(), v = read();
		e[i] = {u, v};
	}
	ans.clear();
	for(int i = 1; i <= n + n + n; ++i)vis[i] = false;
	for(int i = 1; i <= m; ++i){
		int u = e[i].u, v = e[i].v;
		if(vis[u] || vis[v])continue;
		vis[u] = vis[v] = 1;
		ans.push_back(i);
	}
	if(ans.size() >= n){
		printf("Beta2\n");
		for(int i = 0; i < n; ++i)printf("%d ",ans[i]);printf("\n");
		return;
	}
	ans.clear();
	for(int i = 1; i <= n + n + n; ++i)if(!vis[i])ans.push_back(i);
	printf("Beta1\n");
	for(int i = 0; i < n; ++i)printf("%d ",ans[i]);printf("\n");
}

int main(){
	freopen("choice.in","r",stdin);
	freopen("choice.out","w",stdout);
	int t = read();
	for(int i = 1; i <= t; ++i)sol();
	return 0;
}

C. 数塔(pyramid)

二分答案,转化为 1/0

然后观察性质,写成正三角(居中对齐)

如果存在连续的一段(至少两个) 1/0 那么他们上面一定都是他们,而且如果与之相邻的有 1/0 交错,那么他会每次扩展一个

所以当存在连续段时,找到最先扩展到正中间的即为答案

如果全是 1/0 交错,那么直接取两边即可

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 300005;
int n;
int a[maxn], b[maxn];


bool check(int mid){
	for(int i = 1; i <= n + n - 1; ++i)b[i] = a[i] >= mid;
	for(int i = n - 1, j = n + 1; i >= 1 && j <= n + n - 1; --i, ++j){
		if(b[i] == b[i + 1])return b[i];
		if(b[j] == b[j - 1])return b[j];
	}
	return b[1];
}

void sol(){
	n = read();
	for(int i = 1; i <= n + n - 1; ++i)a[i] = read();
	int l = 1, r = n + n - 1, ans;
	while(l <= r){
		int mid = (l + r) >> 1;
		if(check(mid))l = mid + 1, ans = mid;
		else r = mid - 1;
	}
	printf("%d\n",ans);
}

int main(){
	freopen("pyramid.in","r",stdin);
	freopen("pyramid.out","w",stdout);
	int t = read();
	for(int i = 1; i <= t; ++i)sol();
	return 0;
}

D. 环游(tour)

发现能走直接走,不能走才会跳跃,而且最多跳 log

于是对 log 种长度,把直接能到的位置看成一个点

考场想到这里就去考虑树形 DP

考虑状压,每个深度我们只能选择一次,那么状态就压选过的深度

考虑每次我们会从一个子段出发,于是我们还需要走一个前缀和一个后缀,于是分 pre,suf 进行转移

pre[s] 表示使用了 s 深度, 能够拼出的最长前缀

suf 类似,转移考虑新拼上一段未出现的深度

统计答案的时候,枚举前缀使用的状态 s1, 后缀使用其补集,看能否完全覆盖整个区间

因为我们最多跳 log 次,所以区间多于 log 无解,于是总的复杂度为 vlog2v

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; bool f = 0; char c = getchar();
	while(!isdigit(c)){f = c == '-'; c = getchar();}
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return f ? -x : x;
}

const int maxn = 300005;
int n, v, x[maxn];
int l[19][maxn], r[19][maxn];
int pre[1 << 19], suf[1 << 19];
int main(){
	freopen("tour.in","r",stdin);
	freopen("tour.out","w",stdout);
	n = read(), v = read();
	for(int i = 1; i <= n; ++i)x[i] = read();
	int mx = __lg(v) + 1;
	for(int i = 0; i <= mx; ++i){
		l[i][1] = 1;  r[i][n] = n; 
		for(int j = 1 + 1; j <= n; ++j)if(x[j] - x[j - 1] <= (v >> i))l[i][j] = l[i][j - 1]; else l[i][j] = j;
		for(int j = n - 1; j >= 1; --j)if(x[j + 1] - x[j] <= (v >> i))r[i][j] = r[i][j + 1]; else r[i][j] = j;
	}
	memset(suf, 0x3f, sizeof(suf));
	suf[0] = n + 1;
	for(int i = 0; i < (1 << mx); ++i){
		for(int j = 1; j <= mx; ++j){
			if((i >> (j - 1)) & 1)continue;
			pre[i | (1 << (j - 1))] = max(pre[i | (1 << (j - 1))], r[j][min(pre[i] + 1, n)]);
			suf[i | (1 << (j - 1))] = min(suf[i | (1 << (j - 1))], l[j][max(suf[i] - 1, 1)]);
		}
	}
	int cnt = 0;
	for(int i = 1; i <= n; i = r[0][i] + 1) ++cnt;
	if(cnt > mx + 1){
		for(int i = 1; i <= n; ++i)printf("Impossible\n");
	}else{
		for(int i = 1; i <= n; i = r[0][i] + 1){
			bool fl = false;
			for(int j = 0; j < (1 << mx) && !fl; ++j)if(pre[j] >= i - 1 && suf[((1 << mx) - 1) ^ j] <= r[0][i] + 1)fl = 1;
			for(int j = i; j <= r[0][i]; ++j)if(fl)printf("Possible\n");else printf("Impossible\n");
		}
	}
	return 0;
}
posted @   Chen_jr  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示