2022NOIPA层联测3
以暴风骤雨的气势,也像熊熊燃烧的烈火,我将填平所有的深渊,也将照亮所有的夜晚,不到结束的时刻,就是一切未成定局。
问题 A: 【2022NOIP联测3 10月5日】A
大概是我头一次场上写对线段树。%%%Chen_jr的线段树模板,又美观又好背!
发现只有a[1]的排名对他的去留产生影响,所以数据可以用大于/小于 1/0 记录,每个人能力值互不相同就可以把这个数值当成基准线,记录每一年年初时基准线的排名,在变动之后基准线的排名不能超过lim(不能过于靠后),而且每次只会改变某一年中的某一个值,使基准排名增大的影响是不能被消掉的,因为要保证基准不被删除前面的就更能留下,题意就可以转化成对一个数组(由基准每年的排名构成),只有区间+1或-1操作,询问是否每个值都在界内。
然后继续转化,把边界和当前状态作差,每次对差值修改,询问是否存在一个数值小于0.最小值一定最有风险。So--线段树维护区间最小值,支持区间修改。
(因为区间修改只有后缀,我本来还想reverse一下然后用树状数组,忽然发现树状数组维护前缀和但是跟前缀修改似乎关系不大,而差分又只能单点查询……)
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 3;
const ll mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int n, m, q, rnk[maxn], lim[maxn], ls[maxn], B;
vector<bool> ve[maxn];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
struct tree
{
struct node
{
int min, lazy;
}t[maxn<<2];
void pushup(int x)
{
t[x].min = min(t[x<<1].min, t[x<<1|1].min);
}
void build(int x, int l, int r)
{
if(l == r)
{
t[x].min = ls[l]; return;
}
int mid = (l + r) >> 1;
build(x<<1, l, mid);
build(x<<1|1, mid+1, r);
pushup(x);
}
void pushdown(int x)
{
int ls = x << 1, rs = x << 1 | 1;
int lz = t[x].lazy; t[x].lazy = 0;
t[ls].min += lz; t[ls].lazy += lz;
t[rs].min += lz; t[rs].lazy += lz;
}
void update(int x, int l, int r, int L, int R, int val)
{
if(L <= l && r <= R)
{
t[x].min += val; t[x].lazy += val;
return;
}
if(t[x].lazy) pushdown(x);
int mid = (l + r) >> 1;
if(L <= mid) update(x<<1, l, mid, L, R, val);
if(R > mid) update(x<<1|1, mid+1, r, L, R, val);
pushup(x);
}
int query(int x, int l, int r, int L, int R)
{
if(L <= l && r <= R) return t[x].min;
int mid = (l + r) >> 1;
if(t[x].lazy) pushdown(x);
int ans = inf;
if(L <= mid) ans = min(ans, query(x<<1, l, mid, L, R));
if(R > mid) ans = min(ans, query(x<<1|1, mid+1, r, L, R));
return ans;
}
}t;
int main()
{
n = read(); m = read(); q = read();
B = read();
for(int i=1; i<n; i++)
{
int x = read(); if(x > B) rnk[1]++;
}
rnk[1]++;
for(int i=1; i<=m; i++)
{
int b = read(); lim[i] = n-b;
for(int j=1; j<=b; j++)
{
int x = read();
if(x > B) ve[i].push_back(1), rnk[i+1]++;
else ve[i].push_back(0);
}
}
for(int i=2; i<=m; i++) rnk[i] += rnk[i-1];
for(int i=1; i<=m; i++)
{
ls[i] = lim[i] - rnk[i];
}
t.build(1, 1, m);
while(q--)
{
int year = read(), num = read(), val = read();
num--;
if(!ve[year][num])
{
if(val < B)
{
if(t.query(1, 1, m, 1, m) < 0) printf("0\n");
else printf("1\n");
}
else
{
ve[year][num] = 1;
if(year != m) t.update(1, 1, m, year+1, m, -1);
if(t.query(1, 1, m, 1, m) < 0) printf("0\n");
else printf("1\n");
}
}
else
{
if(val > B)
{
if(t.query(1, 1, m, 1, m) < 0) printf("0\n");
else printf("1\n");
}
else
{
ve[year][num] = 0;
if(year != m) t.update(1, 1, m, year+1, m, 1);
if(t.query(1, 1, m, 1, m) < 0) printf("0\n");
else printf("1\n");
}
}
}
return 0;
}
问题 B: 【2022NOIP联测3 10月5日】B
感觉和预设dp有一点相似之处,打算复习一下,翻到了一些优质博客推荐一下大概没关系?
本来正为找不到我的dp的问题而不知所措,直到鹤到了gtm1514的题解,才发现我写了个什么鬼,填数没2倍,3个空格的情况还忘了讨论。调不出来只好交了个dfs:
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 3;
//const ll mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int n, mod, ans;
bool v[35];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
//int a[35], siz;
void dfs(int num)
{
//printf("dfs(%d)\n", num);
//for(int i=1; i<=n; i++) printf("%d ", v[i]);
//printf("\n");
if(num > n)
{
//for(int i=1; i<=siz; i++) printf("%d ", a[i]);
//printf("\n");
ans++; if(ans >= mod) ans -= mod;
return;
}
for(int i=1; i<=n; i++)
{
if(v[i]) continue;
//a[++siz] = i;
v[i] = 1; int x = num+1; bool f1 = 0, f2 = 0;
if(i-2>=1 && !v[i-1] && v[i-2]) x++, v[i-1] = 1, f1 = 1;
if(i+2<=n && !v[i+1] && v[i+2]) x++, v[i+1] = 1, f2 = 1;
dfs(x);
//a[siz--] = 0;
v[i] = 0;
if(f1) v[i-1] = 0;
if(f2) v[i+1] = 0;
}
}
int main()
{
n = read(); mod = read();
dfs(1);
printf("%d\n", ans);
return 0;
}
设dp[i][j]为当前有i个电脑开启,形成了j个连续段的方案数,本来想着连续段当然不会比开启的电脑还多所以第二层循环到i,但其实需要循环到n??洛古题解的版本似乎到i就够了……然后还是鹤:转移的时候有新建段,段扩增,段合并3种。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 402;
//const ll mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int n, mod;
ll f[maxn][maxn];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
inline void add(ll &x, ll y) {x = (x+y)%mod;}
int main()
{
n = read(); mod = read();
f[0][0] = 1;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
if(f[i-1][j-1]) add(f[i][j], j*f[i-1][j-1]%mod);//新建段
if(f[i-1][j]) add(f[i][j], 2*j*f[i-1][j]%mod);//段扩增,向左or向右
if(i>1 && f[i-2][j]) add(f[i][j], 2*j*f[i-2][j]%mod);//中间有1个空位
if(i>1 && f[i-2][j+1]) add(f[i][j], 2*j*f[i-2][j+1]%mod);//中间有两个空位
if(i>2 && f[i-3][j+1]) add(f[i][j], j*f[i-3][j+1]%mod);//中间有3个空位
}
}
printf("%lld\n", f[n][1]);
return 0;
}
问题 C: 【2022NOIP联测3 10月5日】C
暴力居然A了?!就枚举k然后把它拼出来判断字典序……
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 3;
const ll mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int n;
string s, t;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
void work(int k)
{
string fc;
int fl = 1;
for(int i=0; i<(1<<n); i++)
{
fc += s[i^k];
if(fl && s[i^k] > t[i]) return;
if(s[i^k] < t[i]) fl = 0;
}
t = fc;
}
int main()
{
n = read();
cin >> s;
t = s;
for(int i=1; i<(1<<n); i++)
{
work(i);
}
cout << t << endl;
return 0;
}
问题 D: 【2022NOIP联测3 10月5日】D
只鹤懂了官方题解上的特判测试点2……
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 10;
const ll mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int n;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int main()
{
n = read();
printf("1 %d\n", n); printf("1 %d\n", n-1);
for(int i=2; i<=n-2; i++)
{
printf("%d %d\n", i, n);
}
exit(0);
return 0;
}
场上我只想到了dfs枚举每个点的fa,然后建树,挨个判断区间是否合法,Sily极了……
终于鹤到了题解from Chen_jr
构造方式和题解上的有微小的差异,1连n连2,剩下的全都连1。假设所有的好区间只有包含关系,单点也算好区间,就可以把这些互不连通的区间看成互不连通的点,用特殊构造来解决。
如果出现交叉关系,由于区间的交和并必然是好区间,交叉区间的处理独立不干扰。如果某个区间在处理时发现存在比它小的区间的并包含了它,说明已经满足要求不用处理了。
upd:找到的最后一个集合需要用最大元素做代表元。直接赋值的话最大元素左端点已经扔出去了。然后既然已经连边了,merge操作也要配套。
大概就是鹤吧……
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2005;
//const ll mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
vector<int> v;
bool mp[maxn][maxn];
int n, fa[maxn], mi[maxn], mx[maxn];
char c[maxn];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int find(int x)
{
if(x == fa[x]) return x;
return fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
x = find(x); y = find(y);
if(x != y)
{
fa[y] = x;
mi[x] = min(mi[x], mi[y]);
mx[x] = max(mx[x], mx[y]);
}
}
void solve(int l, int r)
{
for(int i=l; i<=r; i=mx[find(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()
{
n = read();
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++) fa[i] = mx[i] = mi[i] = i;
for(int i=2; i<=n; i++)
{
for(int j=1; i+j-1<=n; j++)
{
int l = j, r = j + i - 1;
if(mp[l][r] == 0) continue;
if(find(l) != find(r))
{
if(mx[find(l)] + 1 == mi[find(r)])
{
printf("%d %d\n", l, r);
merge(l, r);
}
else solve(l, r);
}
}
}
return 0;
}