2022NOIPA层联测3

A

发现只关心其相对 \(a_1\) 的大小关系,于是转化为 \(0 / 1\)

然后发现需要保证 \(pre_i + b_i < n\) 就不会被裁

其中 \(pre_i\) 表示 \(\sum_{j = 0}^{i - 1} cnt_1\)

于是线段树维护一下最大值,区间修改即可

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


using namespace std;

typedef long long ll;
typedef unsigned long long ull;

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c >= '0' && c <='9');
	return x;
}

const int maxn = 100005;
int rem, n, m, q, b[maxn], a[maxn], c[maxn];
vector<int>year[maxn];

struct tree{
	struct node{
		int val, tag;
	}t[maxn << 2 | 1];
	void push_down(int x){
		int ls = x << 1, rs = x << 1 | 1;
		t[ls].val += t[x].tag;
		t[rs].val += t[x].tag;
		t[ls].tag += t[x].tag;
		t[rs].tag += t[x].tag;
		t[x].tag = 0;
	}
	void push_up(int x){
		t[x].val = max(t[x << 1].val, t[x << 1 | 1].val);
	}
	void modify(int x, int l, int r, int L, int R, int val){
		if(L <= l && r <= R){
			t[x].val += val;
			t[x].tag += val;
			return;
		}
		if(t[x].tag)push_down(x);
		int mid = (l + r) >> 1;
		if(L <= mid)modify(x << 1, l, mid, L , R, val);
		if(R > mid)modify(x << 1 | 1, mid + 1, r, L, R, val);
		push_up(x);
	}
}t;

int main(){
	n = read(), m = read(), q = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	rem = a[1];
	for(int i = 1; i <= m; ++i){
		b[i] = read();
		for(int j = 1; j <= b[i]; ++j)year[i].push_back(read());
		for(int j = 1; j <= b[i]; ++j)c[i] += (year[i][j - 1] > rem);
	}
	int cpre = 0;
	for(int i = 1; i <= n; ++i)cpre += (a[i] > rem);
	for(int i = 1; i <= m; ++i)t.modify(1, 1, m, i, i, b[i]);
	for(int i = 1; i < m; ++i)t.modify(1, 1, m, i + 1, m, c[i]);
	for(int i = 1; i <= q; ++i){
		int x = read(), y = read(), z = read();
		if(year[x][y - 1] > rem && z < rem && x < m)t.modify(1, 1, m, x + 1, m, -1);
		if(year[x][y - 1] < rem && z > rem && x < m)t.modify(1, 1, m, x + 1, m, 1);
		year[x][y - 1] = z;
		printf("%d\n",(t.t[1].val + cpre < n));
	}
	return 0;
}

B

比较简单的 \(DP\) , 虽然正解 \(n^2\), 但是我太菜了,只会 \(n^3\)

\(dp_{i, j}\) 表示开了 \(i\) 台电脑,其中 \(j\) 台手动开机的方案数

转移考虑枚举第一个自动开机的电脑

\(n^2\)\(DP\) 好像是考虑连续段,涉及新增拼接延展啥的

code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 405;
int n, mod;
int f[maxn][maxn];
int c[maxn][maxn];
int main(){
	cin >> n >> mod;
	for(int i = 0; i <= n; ++i){
		c[i][0] = 1;
		for(int j = 1; j <= i; ++j)
			c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
	}
	f[1][1] = 1;
	for(int i = 2; i <= n; ++i){
		f[i][i] = (f[i - 1][i - 1] + f[i - 1][i - 1]) % mod;
		for(int j = 1; j < i; ++j)
			for(int l = 1; l < j; ++l){
				int r = i - l - 1;
				f[i][j] = (f[i][j] + 1ll * f[l][l] * f[r][j - l] % mod * c[j][l] % mod) % mod;
			}
	}
	int ans = 0;
	for(int i = 1; i <= n; ++i)ans = (ans + f[n][i]) % mod;
	printf("%d\n",ans);
	return 0;
}

C

考虑用类似后缀数组求法的倍增思想

发现每次排序第一关键字是原排名,第二关键字是原排名 \(xor\; w\)

具体为啥,我只能说打表找规律?

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int maxn = (1 << 18) + 55;
int n, len, rk[maxn], ork[maxn], sa[maxn], w;
char s[maxn];
bool cmp(const int &a, const int &b){
	return rk[a] == rk[b] ? rk[a ^ w] < rk[b ^ w] : rk[a] < rk[b];
}
int main(){
	scanf("%d%s",&n,s);
	len = 1 << n;
	for(int i = 0; i < len; ++i)rk[i] = s[i], sa[i] = i;
	for(w = 1; w < len; w <<= 1){
		for(int i = 0; i < len; ++i)ork[i] = rk[i];
		sort(sa, sa + len, cmp);
		int p = 0;
		for(int i = 0; i < len; ++i)
			rk[sa[i]] = (i != 0 && ork[sa[i]] == ork[sa[i - 1]] && ork[sa[i] ^ w] == ork[sa[i - 1] ^ w] ? p : ++p);
		if(p == len)break;
	}
	for(int i = 0; i < len; ++i)printf("%c",s[i ^ sa[0]]);
	return 0;
}

D

投稿了 \(luogu\) 题解,所以无耻引流
\(luogu\) 写的比下面详细多了,而且还有配图

考场发现这题没有大样例,于是先搞他,半小时就交上了

然而假了,不过还好数据水有 \(80\)

特殊性质没过,我应该测一下的。。。。。。

按照长度枚举,然后能合并就加边,是假做法

假的原因可以想想特殊性质

于是每次合并时判断两个集合是否相邻,相邻直接合并,不相邻必然有类似特殊性质的情况

于是可以构造

一个需要注意的点,找到的最后一个集合需要用最大元素做代表元
具体做法请移步 luogu博客

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c >= '0' && c <='9');
	return x;
}

const int maxn = 2005;
int n;
int f[maxn], mi[maxn], mx[maxn];
char c[maxn];
int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
bool mp[maxn][maxn];
void merge(int x, int y){
	x = fa(x); y = fa(y);
	if(x != y){
		f[y] = x;
		mi[x] = min(mi[x], mi[y]);
		mx[x] = max(mx[x], mx[y]);
	}
}
vector<int>v;
void solve(int l, int r){
	for(int i = l; i <= r; i = mx[fa(i)] + 1)v.push_back(i);
	v.back() = r;
	int ls = v.back(), s = v.size();
	for(int i = 0; i < s - 1; ++i)merge(v[i], ls);
	if(v.size() > 1){
		printf("%d %d\n",v[0], v.back());
		printf("%d %d\n",v[1], v.back());
		v.pop_back();
		for(int i = 2; i < v.size(); ++i)printf("%d %d\n",v[i], v[0]);
	}
	v.clear();
}
int main(){
	scanf("%d",&n);
	for(int i = 1; i <= n; ++i){
		scanf("%s",c);
		for(int j = i; j <= n; ++j)mp[i][j] = (c[j - i] == '1');
	}
	for(int i = 1; i <= n; ++i)f[i] = mx[i] = mi[i] = i;
	for(int i = 2; i <= n; ++i){
		for(int j = 1; j + i - 1 <= n; ++j){
			int l = j, r = j + i - 1;
			if(mp[l][r] == 0)continue;
			if(fa(l) != fa(r)){
				if(mx[fa(l)] + 1 == mi[fa(r)]){ 
					printf("%d %d\n",l, r);
					merge(l, r);
				}else solve(l, r);
			}
		}
	}
	
	return 0;
}
posted @ 2022-10-05 16:59  Chen_jr  阅读(56)  评论(6编辑  收藏  举报