2019南昌网络赛I题 Yukino With Subinterval (动态主席树/CDQ)

题目链接 - 计蒜客2019南昌网络赛

一个ACMer萌新 Happier233~

题目简述:

一个长度为n的数组,一共有m次操作,有两种操作:

  1. 修改pos的值为x
  2. 查询[L,R]范围内,权值在[X,Y]范围内的连续线段(线段内的权值相同)个数

n,m<=2e5
内存512MB,时限3.5秒

题目思路:

每次查询[L,R]范围内的线段,其实维护每个线段的开头的值就行了,然后查询[L+1,R]之间又多少个线段的开头,再算上L这个点开头的线段,查询这些线段头有多少是在[X,Y]范围内的,就是答案了。
之前正好在学习CDQ分治,看了看概念什么的,所以第一感觉这就是个CDQ分治啊,可惜网络赛的时候我还没学会CDQ,写不出来,心塞。
然后转头去做动态修改主席树了,每次修改的时候维护pos,pos+1是否是开头,以及变更情况,这样把权值更新进主席树就行了,每次查询[L+1,R]区间内[X,Y]的个数和,再计算L这个位置的值是否在[X,Y]范围内,这样就可以得到答案了。
出题人非常良心的放过了主席树的做法,内存和时间正好可以跑完主席树。
然后我学习网上别人的做法,优化了一下自己的主席树板子,这个代码可以在3秒内跑完,但优化之后可读性–(反正是板子,为了性能将就了)
啊,时隔N个月,发现当初AC的主席树代码怎么T了,难道是计蒜客的评测姬变慢了???顺手更新一下当初已经补完的CDQ分治代码

代码(动态主席树):

// 巨菜的ACMer-Happier233

#include <bits/stdc++.h>

using namespace std;

//-----
typedef double db;
typedef long long ll;
typedef unsigned int ui;
typedef vector<int> vi;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
#define fi first
#define se second
#define pw(x) (1ll << (x))
#define tw(x, k) (((x) >> k) & 1)
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define rep(i, l, r) for(int i=(l);i<(r);++i)
#define per(i, l, r) for(int i=(r)-1;i>=(l);--i)
#define mst(t, v, n) memset(t, v, sizeof(decltype(*(t))) * (n))
#define sf(x) scanf("%d", &(x))
#ifndef ACM_LOCAL
#define endl '\n'
#endif

const int N = int(2e5 + 10);
// m: update count,MAXN>=m*log(n)^2
const int MAXN = int(4e7 + 10);
const int LN = 40;

struct PSegTree {
    const int *a;
    pii ran;
    int n;
    int c[MAXN];
    int tot = 0;
    int lson[MAXN], rson[MAXN];
    // t: static root,s: dynamic root
    int t[N], s[N];

    int build(int l, int r) {
        int k = ++tot;
        c[k] = 0;
        if (l == r) {
            return k;
        }
        int mid = (l + r) >> 1;
        lson[k] = build(l, mid);
        rson[k] = build(mid + 1, r);
        return k;
    }

    // SegTree Range and n points, num can be nullptr
    int init(int l, int r, int _n, const int num[]) {
        tot = 0;
        a = num;
        ran = {l, r};
        n = _n;
        int rt = build(l, r);
        for (int i = 0; i <= n; i++) t[i] = s[i] = rt;
        return rt;
    }

    // update the root in k
    void update(int k[], int rt[], int cnt, int p, int v) {
        // calc
        for (int i = 0; i < cnt; i++)
            c[k[i]] = c[rt[i]] + v;
        int l, r;
        tie(l, r) = ran;
        while (l < r) {
            int mid = (l + r) >> 1;
            // 下面的逗号表达式顺序不能换
            if (p <= mid) {
                // go left
                for (int i = 0; i < cnt; i++) {
                    rson[k[i]] = rson[rt[i]], rt[i] = lson[rt[i]], k[i] = lson[k[i]] = ++tot;
                }
                r = mid;
            } else {
                // go right
                for (int i = 0; i < cnt; i++) {
                    lson[k[i]] = lson[rt[i]], rt[i] = rson[rt[i]], k[i] = rson[k[i]] = ++tot;
                }
                l = mid + 1;
            }
            // calc
            for (int i = 0; i < cnt; i++)
                c[k[i]] = c[rt[i]] + v;
        }
    }

    // build static tree
    inline void change(int pos, int p, int v) {
        if (v == 0) t[pos] = t[pos - 1];
        else { // use int as the int[]
            int rt = t[pos - 1];
            int k = t[pos] = ++tot;
            update(&k, &rt, 1, p, v);
        }
    }

    int use1[LN], use2[LN];

    inline void add(int pos, int p, int v) {
        // memory reuse
        int *k = use1, *rt = use2;
        int cnt = 0;
        for (int i = pos; i <= n; i += (i & -i), cnt++) {
            rt[cnt] = s[i], s[i] = k[cnt] = ++tot;
        }
        update(k, rt, cnt, p, v);
    }

    // calc lson value in use
    inline int sum(int use[], int cnt) {
        int ans = 0;
        for (int i = 0; i < cnt; i++)
            ans += c[lson[use[i]]];
        return ans;
    }

    // calc value in use
    inline int calc(int use[], int cnt) {
        int ans = 0;
        for (int i = 0; i < cnt; i++)
            ans += c[use[i]];
        return ans;
    }

    // ans=p1-p2
    int query(int p1, int p2, int k) {
        int r1 = t[p1], r2 = t[p2];
        int cnt1 = 0, cnt2 = 0;
        // calc root in need
        for (int i = p1; i; i -= (i & -i)) use1[cnt1++] = s[i];
        for (int i = p2; i; i -= (i & -i)) use2[cnt2++] = s[i];
        int l, r;
        tie(l, r) = ran;
        int ans = 0;
        while (l < r) {
            int mid = (l + r) >> 1;
            int cnt = c[lson[r1]] - c[lson[r2]] + sum(use1, cnt1) - sum(use2, cnt2);
            if (k <= mid) {
                // go left
                r1 = lson[r1], r2 = lson[r2];
                for (int i = 0; i < cnt1; i++) use1[i] = lson[use1[i]];
                for (int i = 0; i < cnt2; i++) use2[i] = lson[use2[i]];
                r = mid;
            } else {
                // go right
                ans += cnt;
                r1 = rson[r1], r2 = rson[r2];
                for (int i = 0; i < cnt1; i++) use1[i] = rson[use1[i]];
                for (int i = 0; i < cnt2; i++) use2[i] = rson[use2[i]];
                l = mid + 1;
            }
        }
        int cnt = c[r1] - c[r2] + calc(use1, cnt1) - calc(use2, cnt2);
        ans += cnt;
        return ans;
    }
} tree;

int a[N], c[N];

void solve() {
    int n, q;
    while (cin >> n >> q) {
        for (int i = 1; i <= n; i++) cin >> a[i];
        c[1] = a[1];
        for (int i = 2; i <= n; i++) c[i] = (a[i] == a[i - 1] ? 0 : a[i]);
        tree.init(0, n, n, nullptr);
        for (int i = 1; i <= n; i++) tree.change(i, c[i], c[i] > 0);
        while (q--) {
            int op;
            cin >> op;
            if (op == 1) {
                int p, x;
                cin >> p >> x;
                if (a[p] == x) continue;
                // del p
                if (c[p]) {
                    tree.add(p, c[p], -1);
                    c[p] = 0;
                }
                // add p+1
                if (p < n && a[p] == a[p + 1]) {
                    c[p + 1] = a[p + 1];
                    tree.add(p + 1, c[p], 1);
                }
                if (p < n && x == a[p + 1]) {
                    tree.add(p + 1, c[p + 1], -1);
                    c[p + 1] = 0;
                }
                a[p] = x;
                // add p
                if (p == 1 || a[p - 1] != a[p]) {
                    c[p] = a[p];
                    tree.add(p, c[p], 1);
                }
            } else {
                int l, r, x, y;
                cin >> l >> r >> x >> y;
                int cnty = 0, cntx = 0;
                cnty = tree.query(r, l, y);
                cntx = tree.query(r, l, x - 1);
                int ans = cnty - cntx + (a[l] >= x && a[l] <= y);
                cout << ans << endl;
            }
        }
    }
}

int main() {
#ifdef ACM_LOCAL
    freopen("./data/std.in", "r", stdin);
    // freopen("./data/std.out", "w", stdout);
#else
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#endif

#ifdef ACM_LOCAL
    auto start = clock();
#endif
    int t = 1;
//    cin >> t;
    while (t--)
        solve();
#ifdef ACM_LOCAL
    auto end = clock();
    cerr << "Run Time: " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
#endif
    return 0;
}

###代码(CDQ分治)

// 巨菜的ACMer-Happier233

#include <bits/stdc++.h>

using namespace std;

//-----
typedef double db;
typedef long long ll;
typedef unsigned int ui;
typedef vector<int> vi;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
#define fi first
#define se second
#define pw(x) (1ll << (x))
#define bt(x, k) (((x) >> k) & 1)
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define rep(i, l, r) for(int i=(l);i<(r);++i)
#define per(i, l, r) for(int i=(r)-1;i>=(l);--i)
#define mst(t, v, n) memset(t, v, sizeof(decltype(*(t))) * (n))
#define sf(x) scanf("%d", &(x))
#ifndef ACM_LOCAL
#define endl '\n'
#endif

const int N = int(3e5 + 10);

struct BITree {
    int n;
    ll c[N];

    void init(int _n) {
        n = _n + 1;
        memset(c, 0, sizeof(ll) * n);
    }

    void change(int pos, ll v) {
        for (int i = pos; i < n; i += i & (-i))
            c[i] += v;
    }

    ll query(int x) {
        ll ans = 0;
        for (int i = x; i > 0; i -= i & (-i))
            ans += c[i];
        return ans;
    }

    void update(int l, int r, ll v) {
        change(l, v);
        change(r + 1, -v);
    }
} tree;

struct node {
    // time: 修改时间 | id: 0,1 | f: 正负 | x是下标 | y是权值
    int time, id, f, x, y;
};

bool cmp(const node &a, const node &b) {
    return a.x < b.x;
}

int ans[N];
node p[N << 2], et[N << 2];

void cdq(int l, int r) {
    if (l + 1 == r) return;
    int mid = (l + r) >> 1;
    cdq(l, mid), cdq(mid, r);
    int t = l;
    for (int i = mid; i < r; i++) {
        if (p[i].id)continue;
        for (; t < mid && p[t].x <= p[i].x; t++) {
            if (p[t].id) tree.change(p[t].y, p[t].f);;

        }
        int f = p[i].f;
        int cnt = tree.query(p[i].y);
        ans[p[i].time] += f * cnt;
    }
    // 逆操作p[t].y
    while (--t >= l) {
        if (p[t].id) tree.change(p[t].y, -p[t].f);
    }
    // 归并排序
    int t1 = l, t2 = mid, k = 0;
    while (t1 < mid && t2 < r) {
        et[k++] = p[t1].x < p[t2].x ? p[t1++] : p[t2++];
    }
    copy(p + t1, p + mid, et + k);
    copy(p + t2, p + r, et + k);
    copy(et, et + (r - l), p + l);
}

int a[N];
int c[N];

void solve() {
    int n, q;
    while (cin >> n >> q) {
        a[0] = 0;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
        }
        int tot = 0;
        for (int i = 1; i <= n; i++) {
            if (a[i] != a[i - 1]) {
                p[tot++] = {0, 1, 1, i, a[i]};
            }
        }
        mst(c, 0, n + 1);
        for (int i = 1; i <= q; i++) {
            int op;
            cin >> op;
            if (op == 1) {
                int pos, v;
                cin >> pos >> v;
                ans[i] = -1;
                if (a[pos] == v) continue;
                // del p
                if (a[pos] != a[pos - 1]) {
                    p[tot++] = {i, 1, -1, pos, a[pos]};
                }
                // add pos+1
                if (pos < n && a[pos] == a[pos + 1]) {
                    p[tot++] = {i, 1, 1, pos + 1, a[pos + 1]};
                }
                if (pos < n && v == a[pos + 1]) {
                    p[tot++] = {i, 1, -1, pos + 1, a[pos + 1]};
                }
                a[pos] = v;
                // add pos
                if (pos == 1 || a[pos - 1] != a[pos]) {
                    p[tot++] = {i, 1, 1, pos, a[pos]};
                }
                ans[i] = -1;
            } else {
                ans[i] = 0;
                int l, r, x, y;
                cin >> l >> r >> x >> y;
                if (a[l] >= x && a[l] <= y && a[l] == a[l - 1]) {
                    ans[i] = 1;
                }
                p[tot++] = {i, 0, 1, r, y};
                p[tot++] = {i, 0, -1, r, x - 1};
                p[tot++] = {i, 0, -1, l - 1, y};
                p[tot++] = {i, 0, 1, l - 1, x - 1};
            }
        }
        tree.init(n);
        cdq(0, tot);
        for (int i = 1; i <= q; i++) {
            if (~ans[i]) cout << ans[i] << endl;
        }
    }
}

int main() {
#ifdef ACM_LOCAL
    freopen("./data/std.in", "r", stdin);
    // freopen("./data/std.out", "w", stdout);
#else
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#endif

#ifdef ACM_LOCAL
    auto start = clock();
#endif
    int t = 1;
//    cin >> t;
    while (t--)
        solve();
#ifdef ACM_LOCAL
    auto end = clock();
    cerr << "Run Time: " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
#endif
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值