2022NOIPA层联测10

A 异或(xor)

对于任意数,一定有 x 使得 x xor ai=2m1 于是所有下标必然出现过,否则非法

按照二进制递归处理 l,r

递归深度即考虑到了最高的哪个二进制位

如果 al....mid=amid+1...r 说明他们该位相同,并且可以选择 1/0,对应两个数后面的二进制完全相同

于是答案乘 2 递归一侧

否则,那么一定有 al...mid 该位为 1, amid+1..r 该位为 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 = (1 << 16) + 500;
const int mod = 1e9 + 7;
int n, m, a[maxn];
int ans = 1, tim;
int vis[maxn];
void solve(int l, int r){
	if(l >= r || ans == 0)return;
	int mid = (l + r) >> 1;
	bool fl = 1;
	for(int i = l; i <= mid; ++i)if(a[i] != a[mid - l + 1 + i]){fl = 0;break;}
	if(fl)ans = (ans + ans) % mod;
	else{
		++tim;
		for(int i = l; i <= mid; ++i)vis[a[i]] = tim;
		for(int i = mid + 1; i <= r; ++i)if(vis[a[i]] == tim){ans = 0; return;}
		solve(l, mid);
	}
	solve(mid + 1, r);
}
int main(){
	n = read(), m = read();
	for(int i = 0; i < (1 << m); ++i)a[i] = read();
	for(int i = 0; i < (1 << m); ++i)vis[a[i]] = -1;
	for(int i = 1; i <= n; ++i)if(vis[i] == 0){ans = 0;break;}
	solve(0, (1 << m) - 1);
	printf("%d\n",ans);
	return 0;
}

B 图论(graph)

二分答案,每次暴力 check n4logn 能过

我加上了一点小优化变成了 n3logn,没啥大用,反而跑的更慢

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 = 505;
int mp[maxn][maxn], d0[maxn], d[maxn], np[maxn][maxn], ans;
int n, m;
queue<int>q;
bool vis[maxn];
bool check(int lim){
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= n; ++j)
			np[i][j] = mp[i][j];
	for(int i = 1; i <= n; ++i)d[i] = d0[i], vis[i] = true;
	for(int i = 1; i <= n; ++i)q.push(i);
	while(!q.empty()){
		int x = q.front(); q.pop();
		if(d[x] >= n - 1)continue;
		vis[x] = false;
		for(int i = 1; i <= n; ++i)if(i != x && np[i][x] == 0 && d[i] + d[x] >= lim){
			np[i][x] = np[x][i] = 1; ++d[i]; ++d[x];
			if(!vis[i] && d[i] < n - 1)q.push(i), vis[i] = true;
			if(!vis[x] && d[x] < n - 1)q.push(x), vis[x] = true;
		}
	}
	// bool fl = 1;
	// while(fl){
	// 	fl = 0;
	// 	for(int i = 1; i <= n; ++i)if(d[i] < n - 1)
	// 		for(int j = 1; j <= n; ++j)if(d[j] < n - 1)
	// 			if(i != j && np[i][j] == 0 && d[i] + d[j] >= lim){
	// 				np[i][j] = np[j][i] = 1;
	// 				++d[i]; ++d[j];
	// 				fl = 1;
	// 			}
	// }
	for(int i = 1; i <= n; ++i)if(d[i] < n - 1)return false;
	return true;
}

int main(){
	n = read(); m = read();
	for(int i = 1; i <= m; ++i){
		int u = read(), v = read();
		mp[u][v] = mp[v][u] = 1;
		++d0[u]; ++d0[v];
	}
	int l = 0, r = n + n;
	while(l <= r){
		int mid = (l + r) >> 1;
		if(check(mid))l = mid + 1, ans = mid;
		else r = mid - 1;
	}
	printf("%d\n",ans);
	return 0;
}

C 视频(video)

这比某猪国杀难写

预处理操作序列,然后依题意模拟即可

写挂了好多。。

播放与内存不是完全割裂的。你可能为了内存等一段时间再编译。于是完全割裂无法处理

最后借鉴(he)了老殷的优秀写法

存一个每一帧编译完的时间,动态让内存清除元素,外层循环进行播放,

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
#define int long long
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;
const ll inf = 1e18;
int n, d, a[maxn], pre[maxn], nxt[maxn], rd[maxn];
char c[maxn];
int id[maxn], p, nd[maxn];
ll tim[maxn];
bool vis[maxn];
bool check(int mid){
	int p = mid + 1, size = mid;
	for(int i = 1; i <= n; ++i)nd[i] = rd[i], tim[i] = inf;
	for(int i = 1; i <= mid; ++i){
		tim[id[i]] = 0;
		if(pre[id[i]])--nd[pre[id[i]]];
		if(nxt[id[i]])--nd[nxt[id[i]]];
	}
	for(int i = 1; i <= n; ++i){
		if(1ll * d * (i - 1) < tim[i])return false;
		if(nd[i] == 0)--size;
		while(size < mid && p <= n){
			if(int v = pre[id[p]]){--nd[v]; if(v <= i && nd[v] == 0)--size;}
			if(int v = nxt[id[p]]){--nd[v]; if(v <= i && nd[v] == 0)--size;}
			tim[id[p]] = max(d * (i - 1), tim[id[p - 1]]) + a[id[p]];
			++size; ++p;
		}
	}
	return true;
}
void in(int i){
	if(c[i] == 'B' && !vis[nxt[i]])in(nxt[i]);
	id[++p] = i; vis[i] = true;
}
signed main(){
	n = read(); d = read();
	scanf("%s",c + 1);
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i){
		if(c[i] != 'I'){
			if(c[i - 1] == 'B') pre[i] = pre[i - 1];
			else  pre[i] = i - 1; 
			++rd[pre[i]];
		}
	}
	for(int i = n; i >= 1; --i)if(c[i] == 'B'){
		if(c[i + 1] == 'B')nxt[i] = nxt[i + 1];
		else nxt[i] = i + 1;
		++rd[nxt[i]];
	}
	for(int i = 1; i <= n; ++i)if(!vis[i])in(i);
	int l = 1, r = n, ans = n;
	while(l <= r){
		int mid = (l + r) >> 1;
		if(check(mid))r = mid - 1, ans = mid;
		else l = mid + 1;
	}
	printf("%lld\n",ans);
	return 0;
}

D 交换(swap)

考场完全没细想,其实是个水题

把交换的贡献在小数那里计算

考虑一个数移到左侧/右侧,需要交换的次数为左侧/右侧比他大的数,与别的数选择没有任何关系

于是处理一下,取min即可

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 = 500005;
int n, a[maxn], le[maxn], re[maxn];
struct BIT{
	int t[maxn];
	int lowbit(int x){return x & -x;}
	void add(int x){while(x <= n){++t[x];x += lowbit(x);}}
	int query(int x){int ans = 0; while(x){ans += t[x]; x -= lowbit(x);}return ans;}
	void clear(){for(int i = 1; i <= n; ++i)t[i] = 0;}
}t;
int main(){
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)le[i] = t.query(n) - t.query(a[i]), t.add(a[i]);
	t.clear();
	for(int i = n; i >= 1; --i)re[i] = t.query(n) - t.query(a[i]), t.add(a[i]);
	ll ans = 0;
	for(int i = 1; i <= n; ++i)ans += min(le[i], re[i]);
	printf("%lld\n",ans);
	return 0;
}
posted @   Chen_jr  阅读(35)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示