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;
}