ACM-ICPC 2019南昌网络赛I题 Yukino With Subinterval

ACM-ICPC 2019南昌网络赛I题 Yukino With Subinterval

题目大意:给一个长度为n,值域为[1, n]的序列{a},要求支持m次操作:

  1. 单点修改
    1 pos val
  2. 询问子区间中某个值域的数的个数,连续的相同数字只记为一个。(即统计数字段的个数)
    2 L R x y

数据范围:

1 ≤ n,m ≤ 2×10^5

1 ≤ a[i] ≤ n


解题思路:

  • 连续重复的数字只记一次。所以考虑将每个数字段除第一个出现外的数字都删去(记为0)。在读入操作的时候暴力模拟,同时维护一个新序列,把原序列的连续重复数字在新序列都记为0。
  • 询问在位置上和值域上都是可作差的。所以将原询问拆分为4个关于(t, pos, val)的子询问
  • 将初值也转换为修改操作
  • 转化成了一个带修改的二维偏序问题;算上时间序就是一个三维偏序

细节:

  • 在修改a[i]时要分别考虑删除原数与添加新数对a[i - 1]和a[i + 1]的影响。删除和添加分开讨论会比较方便
if (pos != n && a[pos] == a[pos + 1])
    A[cnt++] = (ACT) {i, pos + 1, a[pos], 1, 1, 0};
if (pos == 1 || a[pos] != a[pos - 1])
    A[cnt++] = (ACT) {i, pos, a[pos], 1, -1, 0};
a[pos] = val;
if (pos != n && val == a[pos + 1])
    A[cnt++] = (ACT) {i, pos + 1, val, 1, -1, 0};
if (pos == 1 || val != a[pos - 1])
    A[cnt++] = (ACT) {i, pos, val, 1, 1, 0};
  • 要特判每个询问的最左端点,如果是0,且会对答案有影响,(在值域范围内)要给答案+1
ans[Q] = (l == 1 || a[l] != a[l - 1]) ? 0 : 1;
if (a[l] < x || a[l] > y)   ans[Q] = 0; //无需修正
  • 分治时,右半边的修改操作不需要再应用(其对右半边的查询已经被统计,对左半边的查询无影响,)
  • 写分治的题时有几个不错的调试输出点,可以很快地找到错误:
    1. 树状数组的修改、查询函数开头处
    2. 查询操作的统计答案处
    3. 递归头部输出L、R,确定函数调用的状态


CDQ真的快。考场上队友用树套树,手写Treap + 读优还是被卡常,事后CDQ分治连cin都不用改就过了·_·!


AC代码:

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int maxn = 2e5 + 1;
int cnt, ans[maxn], Q, a[maxn], n, sym[maxn << 3];
int _v[maxn];
struct ACT {
    int t, pos, val, opt, pls, id;
    bool operator<(ACT &rhs) const {
        return t < rhs.t || (t == rhs.t &&             (pos < rhs.pos || (pos == rhs.pos &&             (val < rhs.val || (val == rhs.val &&             opt < rhs.opt)))));
    }
}A[maxn << 3], T[maxn << 3];

int lowbit(int x) {
    return x & (-x);
}
void add(int x, int v) {
    if (x == 0)
        return;
    //printf("add: %d, %d\n", x, v);
    while (x <= n) {
        _v[x] += v;
        x += lowbit(x);
    }
}
int query(int x) {
    int ret = 0;
    while (x) {
        ret += _v[x];
        x -= lowbit(x);
    }
    return ret;
}
void read() { //(A) {t, pos, val, opt, pls, id}
    int m, i, l, r, x, y, opt, pos, val;
    cin>>n>>m;
    for (i = 0; i < n; ++i) {
        cin>>a[i + 1];
        if (a[i + 1] != a[i])
            A[cnt++] = (ACT) {-1, i + 1, a[i + 1], 1, 1, 0};
    }
    for (i = 0; i < m; ++i) {
        cin>>opt;
        if (opt == 1) {
            cin>>pos>>val;
            if (pos != n && a[pos] == a[pos + 1])
                A[cnt++] = (ACT) {i, pos + 1, a[pos], 1, 1, 0};
            if (pos == 1 || a[pos] != a[pos - 1])
                A[cnt++] = (ACT) {i, pos, a[pos], 1, -1, 0};
            a[pos] = val;
            if (pos != n && val == a[pos + 1])
                A[cnt++] = (ACT) {i, pos + 1, val, 1, -1, 0};
            if (pos == 1 || val != a[pos - 1])
                A[cnt++] = (ACT) {i, pos, val, 1, 1, 0};
        } else {
            cin>>l>>r>>x>>y;
            ans[Q] = (l == 1 || a[l] != a[l - 1]) ? 0 : 1;
            if (a[l] < x || a[l] > y)
                ans[Q] = 0;
            A[cnt++] = (ACT) {i, r, y, 2, 1, Q};
            A[cnt++] = (ACT) {i, r, x - 1, 2, -1, Q};
            A[cnt++] = (ACT) {i, l - 1, y, 2, -1, Q};
            A[cnt++] = (ACT) {i, l - 1, x - 1, 2, 1, Q++};
        }
    }
    sort(A, A + cnt);
}
void mrdge(int l, int r, ACT* T) {
    if (r - l <= 1) return;
    int mid = l + (r - l) / 2, p = l, q = mid, tot = 0;
    //printf("###l = %d, r = %d\n", l, r);
    mrdge(l, mid, T);   mrdge(mid, r , T);
    //printf("***l = %d, r = %d\n", l, r);
    if (query(n) != 0)
        cout<<"! BIT = "<<query(n)<<endl;
    while (p < mid || q < r) {
        if (q >= r || (p < mid && (A[p].pos < A[q].pos || (A[p].pos == A[q].pos &&             (A[p].val < A[q].val || (A[p].val == A[q].val &&             A[p].opt < A[q].opt)))))) {
            if (A[p].opt == 1) {
                add(A[p].val, A[p].pls);
                sym[tot] = 1;
            }
            T[tot++] = A[p++];
        }
        else {
            if (A[q].opt == 2) {
                ans[A[q].id] += query(A[q].val) * A[q].pls;
                //printf("querying[%d]...ans[%d] = %d\n", A[q].val, A[q].id, ans[A[q].id]);
            }
            T[tot++] = A[q++];
        }
    }
    for (int i = 0; i < tot; ++i) {
        if (T[i].opt == 1 && sym[i]) {
            add(T[i].val, -T[i].pls);
            sym[i] = 0;
        }
        A[l + i] = T[i];
    }
    return;
}
void print() {
    for (int i = 0; i < Q; ++i)
        cout<<ans[i]<<endl;
    return;
}
signed main(){
    read();
    mrdge(0, cnt, T);
    print();
    return 0;
}

原文地址:https://www.cnblogs.com/Decisive/p/11517635.html

时间: 2024-08-28 20:44:24

ACM-ICPC 2019南昌网络赛I题 Yukino With Subinterval的相关文章

2019 南昌网络赛icpc I题 cdq分治或分块

题意:给你一个数组,然后每次有两种操作,操作一是修改数组里的数,操作二是查询区间[ l , r ] 里有多少个子区间满足以下条件:1.子区间内的数全部相同.2.子区间内的数在x到y之间.3.子区间得是不能延伸的. 题目链接:https://nanti.jisuanke.com/t/41356 题解:首先转化问题,设 b[ i ] = a[i]==a[i-1] ? 0 : a[i],然后问题就变成了问询区间内有多少个x到y之间的数.(注意左端点特判)这不就是主席树....带修改...好,树状数组加

hdu 4431 第37届ACM/ICPC 天津赛区现场赛A题 枚举

题意:就是给了13张牌.问增加哪些牌可以胡牌.m是数字,s是条,p是筒,c是数字 胡牌有以下几种情况: 1.一个对子 +  4组 3个相同的牌或者顺子.  只有m.s.p是可以构成顺子的.东西南北这样的牌没有顺子. 2.7个不同的对子. 3.1m,9m,1p,9p,1s,9s,1c,2c,3c,4c,5c,6c,7c.  这13种牌每种都有,而且仅有这13种牌.肯定是有一种2张.其他的1张. 模拟即可,第一个对子的情况需要枚举 很麻烦的模拟,但是貌似稳银的很需要这题,所以这种难度必须要弄懂,加油

2014 ACM/ICPC 鞍山赛区网络赛(清华命题)

为迎接10月17号清华命题的鞍山现场赛 杭电上的题目 Biconnected(hdu4997)      Rotate(hdu4998)     Overt(hdu4999)   Clone(hdu5000)   Walk(hdu5001)   LianLianKan   Rescue   Spy's Work   Color the Tree   The Ghost Blows Light   USACO ORZ   2013/8/27

2017 acm icpc 沈阳(网络赛)5/12 题解

比赛中较...能做的5道题 hdoj6195. cable cable cable 题目链接 : http://acm.hdu.edu.cn/showproblem.php?pid=6195 题目大意 : 略 规律 : 答案 = k+(m-k)*k hdoj6198. number number number 题目链接 : http://acm.hdu.edu.cn/showproblem.php?pid=6198 题目大意  : 给你一个整数n.问你n个斐波那契数(可重复)不能构成哪些数,输出

2019南昌网络赛-I(单调栈+线段树)

题目链接:https://nanti.jisuanke.com/t/38228 题意:定义一段区间的值为该区间的和×该区间的最小值,求给定数组的最大的区间值. 思路:比赛时还不会线段树,和队友在这题上弄了3小时,思路大体都是对的,但就是没法实现.这几天恶补线段树. 首先可以利用单调栈来查找满足a[i]为最小值的最大区间L[i]~R[i].然后利用线段树求一个段的和sum.最小前缀lsum和最小后缀rsum.然后遍历a[i]: a[i]>0:最优为sum(L[i],R[i])*a[i] a[i]<

2019南昌网络赛-I. Yukino With Subinterval 线段树套树状数组

TMD...这题卡内存卡的真优秀... 所以以后还是别用主席树的写法...不然怎么死的都不知道... 树套树中,主席树方法开权值线段树...会造成空间的浪费...这道题内存卡的很紧... 由于树套树已经不需要持久化了,直接动态开点就完事了...用主席树方法开过不去,要么超内存,要么越界... 大概思路...这题要求的[L,R]区间内,满足x<=a[i]<=y的连续的段数, 这题大概是个套路题,我们很容易想到,我们把连续的区间变成单点,把一段区间,类似1 1 1 3 5 变成 1 0 0 3 5

zoj 3659 第37届ACM/ICPC 长春赛区现场赛E题 (并查集)

题意:给出一棵树,找出一个点,求出所有点到这个点的权值和最大,权值为路径上所有边权的最小值. 用神奇的并查集,把路按照权值从大到小排序,然后用类似Kruskal的方法不断的加入边. 对于要加入的一条路,这条路连接这城市x和y,x所在的集合为A, y所在的集合为B, 可以确定A,B集合内的所有路都比当前这条路的权值大.如果让集合B加入集合A,就是让中心城市位于集合A,那么可以确定这两个集合合并之后的总权值为: A的权值总和+B的数量*当前这条路的权值.同样算出让集合B加入集合A的情况,取两者合并后

hdu 4438 第37届ACM/ICPC 天津赛区现场赛H题

题意:Alice和Bob两个人去打猎,有两种(只)猎物老虎和狼: 杀死老虎得分x,狼得分y: 如果两个人都选择同样的猎物,则Alice得分的概率是p,则Bob得分的概率是(1-p): 但是Alice事先知道Bob先选老虎的概率是Q,问Alice得分的期望最大值是 求期望 如果先去打老虎,则会有bob先去打狼和bob去打老虎两种情况,期望相加则是alice去打老虎的期望,然后求打狼的期望,比较大小即可 1 #include<cstdio> 2 #include<iostream> 3

2019 ICPC 南昌网络赛

2019 ICPC 南昌网络赛 比赛时间:2019.9.8 比赛链接:The 2019 Asia Nanchang First Round Online Programming Contest 总结 // 史上排名最高一次,开场不到两小时队友各A一题加水题共四题,排名瞬间升至三四十名 // 然后后三小时就自闭了,一题都没有突破...最后排名211 hhhh ? ? B. Fire-Fighting Hero 题意 队友做的,待补. ? AC代码 #include<cstdio> #includ