2022NOIP A层联测19 皮胚 核冰 方珍 术劣
[区间计数DP]T1:给出通配符串B和字符串A,B中'.'代表任意字符匹配,'*'代表复制k个和前一个一样的字符(k>=0),求B可以最多和A的多少前缀匹配。(n<=5000)
正解:\(f[i][j]:代表ai和bj匹配的可行性,分类讨论bj的可能转移。O(n^2)\)
[数据结构:线段树二分]T2:对于给定的Ai序列,可以对于重复的数[a,b]删除,加入[a+1],给出K次操作把原序列A改变一个位置,求每次操作之后的最后剩下的数的最多种类
法一:
可持久化序列?非常巧妙地思路,对于Ai贪心向大分配时,不改变Ai的值,相当于一个历史状态
,之后再更新i+1位置直接继承i就可以了,如果change_i==a_i就break继承原答案,妙!60points纯暴力!
法二:
模拟这个过程,线段树优化。
考虑用线段树维护该贪心,修改操作可以看成是一次删除和一次插入操作,设每一个
数出现的次数为 cnti。
对于插入操作,设插入一个数 x。
增加一个位置的值可能导致连续的多次修改,从 x 开始 cntx = 2 的数都会在加入一个
数之后被合并(最终 cnti = 1),最后一个数出现次数 +1。
对于删除操作,设删除一个数 x。
在删除 x 时,需要考虑从 x + 1 的位置取回一个数。x + 1 可以被取回的条件为:
- 删除操作之后 cntx = 0。
- 曾经合并得到 x + 1 过。
额外维护一棵线段树表示每一个数 x 合并的次数,在两棵线段树上二分即可找到位置。
其余操作均可以归纳为区间加法。
点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define ll long long
#define ull unsigned long long
#define chu printf
inline ll re() {
ll x = 0, h = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
h = -1;
ch = getchar();
}
while (ch <= '9' && ch >= '0') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * h;
}
const int N = 5e5 + 100;
const int maxn = 5e5 + 30;
int n, m, a[N], cor[N], rat[N];
struct Seg {
#define lson (rt << 1)
#define rson (rt << 1 | 1)
int minn[N << 2], pol[N << 2][2], cov[N << 2][2];
inline void pushup(int rt) {
minn[rt] = min(minn[lson], minn[rson]);
pol[rt][0] = pol[lson][0] + pol[rson][0];
pol[rt][1] = pol[lson][1] + pol[rson][1];
}
inline void pushdown(int rt, int l, int mid, int r) {
if (cov[rt][0]) {
cov[lson][0] += cov[rt][0];
cov[rson][0] += cov[rt][0];
minn[lson] += cov[rt][0];
minn[rson] += cov[rt][0];
cov[rt][0] = 0;
}
if (cov[rt][1] != -1) {
cov[lson][1] = cov[rson][1] = cov[rt][1];
pol[lson][cov[rt][1]] = mid - l + 1;
pol[lson][!cov[rt][1]] = 0;
pol[rson][cov[rt][1]] = r - (mid + 1) + 1;
pol[rson][!cov[rt][1]] = 0;
cov[rt][1] = -1;
}
}
inline void build(int rt, int l, int r) {
minn[rt] = 1e9;
cov[rt][0] = 0;
cov[rt][1] = -1;
if (l == r) {
minn[rt] = rat[l]; //没有进位就没有出现意义
if (cor[l])
pol[rt][cor[l] - 1] = 1;
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
pushup(rt);
}
inline int query2(int rt, int l, int r, int L, int R, int lim) //删数
{
// chu("get:%d\n",rt);
if (l == r) {
if (!pol[rt][lim] || (minn[rt] == 0 && lim == 0))
return l;
return 1e9;
}
int mid = (l + r) >> 1;
pushdown(rt, l, mid, r);
if (L == l && r == R) {
if (pol[lson][lim] == mid - l + 1 && (minn[lson] || lim))
return query2(rson, mid + 1, r, mid + 1, R, lim);
return query2(lson, l, mid, L, mid, lim);
}
if (R <= mid)
return query2(lson, l, mid, L, R, lim);
if (L > mid)
return query2(rson, mid + 1, r, L, R, lim);
return min(query2(lson, l, mid, L, mid, lim), query2(rson, mid + 1, r, mid + 1, R, lim));
}
inline void update(int rt, int l, int r, int L, int R, int cv, int sig) {
if (L > R)
return;
int mid = (l + r) >> 1;
if (L <= l && r <= R) {
// chu("for:%d(%d %d)\n",sig,l,r);
if (sig == 1) {
cov[rt][1] = cv;
pol[rt][cv] = r - l + 1;
pol[rt][!cv] = 0;
} else if (sig == 2) {
if (cv == 1) {
if (pol[rt][0])
pol[rt][0] = 0, pol[rt][1] = 1;
else
pol[rt][0] = 1, pol[rt][1] = 0;
} else {
if (pol[rt][1])
pol[rt][1] = 0, pol[rt][0] = 1;
else
pol[rt][0] = pol[rt][1] = 0;
}
} else {
minn[rt] += cv;
cov[rt][0] += cv;
}
// chu("%d %d %d\n",minn[rt],pol[rt][0],pol[rt][1]);
return;
}
pushdown(rt, l, mid, r);
if (L <= mid)
update(lson, l, mid, L, R, cv, sig);
if (R > mid)
update(rson, mid + 1, r, L, R, cv, sig);
pushup(rt);
}
} T;
int main() {
freopen("merge.in", "r", stdin);
freopen("merge.out", "w", stdout);
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
n = re(), m = re();
for (rint i = 1; i <= n; ++i) a[i] = re(), cor[a[i]]++;
for (rint i = 1; i < maxn; ++i) {
if (!cor[i])
continue;
rat[i] = (cor[i] - 1) >> 1;
cor[i + 1] += rat[i];
cor[i] -= (rat[i] << 1);
}
T.build(1, 1, maxn);
for (rint i = 1; i <= m; ++i) {
int opt = re();
if (opt == 1) {
int pos = re(), val = re();
int sd = T.query2(1, 1, maxn, a[pos], maxn, 0);
// chu("pos:%d\n",sd);
// chu("sd :%d chec1 %d\n",sd,T.pol[1][0]+T.pol[1][1]);
T.update(1, 1, maxn, a[pos], sd - 1, 1, 1);
T.update(1, 1, maxn, sd, sd, -1, 2);
T.update(1, 1, maxn, a[pos], sd - 1, -1, 0);
a[pos] = val;
sd = T.query2(1, 1, maxn, a[pos], maxn, 1);
T.update(1, 1, maxn, a[pos], sd - 1, 0, 1);
T.update(1, 1, maxn, sd, sd, 1, 2);
T.update(1, 1, maxn, a[pos], sd - 1, 1, 0);
} else {
chu("%d\n", T.pol[1][0] + T.pol[1][1]);
}
}
return 0;
}
/*
5 9
1 2 3 4 5
2
1 1 2
2
1 3 2
2
1 4 3
2
1 5 3
2
*/
[Mex问题+剪枝+值域观察优化]T3:给出n个序列Ai,对于每个序列的价值是Ai的子串的mex的第k小的值+\(w_{ai}\),求\(max(val_{mex_{ki}}+w_{ai})\)(n<=5e5)
正解
发现w很大,n很小,所以w的大小是决定性因素,把序列按w排序,从大到小枚举算value,如果\(m+w<=nowans\)直接跳过
,对于 区间第k大mex,可以转化成计算区间mex>=k的个数,枚举R,尺缩取L,使得[L,R]包含0~mid-1的所有数值
然后就可以A了
但是注意到当确定了一个·答案anse,下一个序列会更优当且仅当k_mex=\(is legal(anse-w+1)\),所以设置成起点直接向上枚举,最多有n个以anse为起点的>=anse的取值(w递减),所以复杂度\(O(n^2)\)
[线段树+关于等差数列的性质观察维护+复杂度分析优化过程]T4:给出长度是n的序列A,求对于每一个前缀的子序列的等差序列的数量。(n<=2e5)
考场
发现\(n^2\)枚举出来会有很多分,于是想瞎搞一下等差数列的性质,取差值的min作为公差,算了min和max符合要求与否,还break了一下,结果都假了,因为公差这么算会大得离谱,break不满足性质(小的不是大的可能是)
瞎搞+暴力
【1】会优美的瞎搞50pts
倒着break,虽然也不对,但是随机数据下大部分是对的
【2】会推式子40pts
用2种类似hash的方式判断等差数列【1】是等差求和【2】是等差平方求和
【3】会观察性质40pts
发现序列公差就是22差值的gcd,只要\(max-min=(len*d)\)就可以。
正解
线段树维护\(max-min+l*d=r*d\),维护min,因为max-min+l * d>=r * d
对于gcd的维护,相同段并查集合并,修改的时候对于改变的·直接单点,
因为最多变化log次gcd所以均摊复杂度\(O(n*logn^2)\)
点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define ll long long
#define ull unsigned long long
#define chu printf
inline ll re() {
ll x = 0, h = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
h = -1;
ch = getchar();
}
while (ch <= '9' && ch >= '0') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * h;
}
const int N = 2e5 + 100;
int n, a[N], gg[N], fa[N], mx[N], mi[N], t1, t2, l[N];
ll ans;
inline int gcd(int x, int y) {
if (!y)
return x;
return gcd(y, x % y);
}
inline int father(int x) {
if (x == fa[x])
return x;
return fa[x] = father(fa[x]);
}
struct seg {
#define lson (rt << 1)
#define rson (rt << 1 | 1)
int minn[N << 2], cnt[N << 2], tag[N << 2];
inline void pushup(int rt) {
minn[rt] = min(minn[lson], minn[rson]);
cnt[rt] = 0;
if (minn[rt] == minn[lson])
cnt[rt] += cnt[lson];
if (minn[rt] == minn[rson])
cnt[rt] += cnt[rson];
}
inline void pushdown(int rt) {
if (tag[rt] == 0)
return;
minn[lson] += tag[rt];
minn[rson] += tag[rt];
tag[lson] += tag[rt];
tag[rson] += tag[rt];
tag[rt] = 0;
}
inline void build(int rt, int l, int r) {
if (l == r) {
cnt[rt] = 1;
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
pushup(rt);
}
inline void update(int rt, int l, int r, int L, int R, int val) {
// chu("minn[5]:%d\n",minn[5]);
if (L <= l && r <= R) {
// chu("%d--%d:add:%d\n",l,r,val);
tag[rt] += val;
minn[rt] += val;
return;
}
pushdown(rt);
int mid = (l + r) >> 1;
if (L <= mid)
update(lson, l, mid, L, R, val);
if (R > mid)
update(rson, mid + 1, r, L, R, val);
pushup(rt);
}
inline int query(int rt, int l, int r, int L, int R, int goal) {
// chu("arrive:(%d)%d--%d goal:%d(minn[]:%d)\n",rt,l,r,goal,minn[rt]);
if (l > r)
return 0;
if (l == r)
return minn[rt] == goal;
if (L <= l && r <= R && minn[rt] >= goal) {
if (minn[rt] == goal)
return cnt[rt];
return 0;
}
pushdown(rt);
int mid = (l + r) >> 1;
int sr = 0;
if (L <= mid)
sr += query(lson, l, mid, L, R, goal);
if (R > mid)
sr += query(rson, mid + 1, r, L, R, goal);
return sr;
}
} t;
int main() {
freopen("sequence.in", "r", stdin);
freopen("sequence.out", "w", stdout);
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
n = re();
for (rint i = 1; i <= n; ++i) a[i] = re(), fa[i] = i, l[i] = i;
t.build(1, 1, n); //维护每个位置的值?
ans = 1;
chu("%lld ", ans);
mi[++t1] = 1;
mx[++t2] = 1;
for (rint i = 2; i <= n; ++i) {
++ans;
while (t1 && a[mi[t1]] >= a[i]) {
t.update(1, 1, n, mi[t1 - 1] + 1, mi[t1], a[mi[t1]] - a[i]);
// chu("upd:%d--%d:%d\n",mi[t1-1]+1,mi[t1],mi[t1]-a[i]);
--t1;
}
while (t2 && a[mx[t2]] <= a[i]) {
t.update(1, 1, n, mx[t2 - 1] + 1, mx[t2], a[i] - a[mx[t2]]);
--t2;
}
mi[++t1] = i;
mx[++t2] = i;
// chu("query(%d):%d\n",2,t.query(1,1,n,2,2,8));
gg[i - 1] = abs(a[i] - a[i - 1]);
// chu("update(%d--%d):%d\n",i-1,i-1,gg[i-1]*(i-1));
t.update(1, 1, n, i - 1, i - 1, gg[i - 1] * (i - 1));
int las = i - 1;
int now = gg[i - 1];
// chu("query(%d):%d\n",2,t.query(1,1,n,2,2,8));
for (rint p = i - 2; p >= 1; --p) {
p = father(p);
int gd = gcd(now, gg[p]);
// chu("for:%d--%d:gcd_new:%d the old:%d\n",l[p],las-1,gd,gg[p]);
if (gd != gg[p]) {
// chu("change?\n");
for (rint j = l[p]; j < las; ++j) t.update(1, 1, n, j, j, j * gd - j * gg[p]);
gg[p] = gd;
}
if (gg[p] == gg[father(las)]) {
int rf = father(las);
fa[rf] = p;
l[p] = min(l[p], l[rf]);
}
las = p = l[p]; //更新端点
}
// chu("query(%d):%d\n",2,t.query(1,1,n,2,2,8));
las = i;
// chu("check:%d\n",i);
for (rint p = i - 1; p >= 1; --p) {
p = father(p);
ans += t.query(1, 1, n, l[p], las - 1, i * gg[p]);
// chu("for:%d--%d check:%d is
// legal?:get:%d\n",l[p],las-1,i*gg[p],t.query(1,1,n,l[p],las-1,i*gg[p]));
p = las = l[p];
}
chu("%lld ", ans);
// chu("\n2 the problem:%d\n",t.query(1,1,n,2,2,6));
}
return 0;
}
/*
4
4 3 5 1
1 3 6 9
*/