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 可以被取回的条件为:

  1. 删除操作之后 cntx = 0。
  2. 曾经合并得到 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
*/

posted on 2022-11-01 20:53  HZOI-曹蓉  阅读(30)  评论(0编辑  收藏  举报