2022NOIP A层联测16

A. 平衡(balance)

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

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\) 类似,转移考虑新拼上一段未出现的深度

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

因为我们最多跳 \(log\) 次,所以区间多于 \(log\) 无解,于是总的复杂度为 \(vlog^2v\)

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 @ 2022-10-27 16:23  Chen_jr  阅读(40)  评论(0)    收藏  举报