跳转至

CF2096 题解

CF2096C Wonderful City

给你一个 \(n\times n\) 的矩阵 \(h\),你可以花费 \(a_i\) 的代价将第 \(i\) 行的所有数字增加 \(1\),或者花费 \(b_j\) 的代价将第 \(j\) 列的所有数字增加 \(1\),使得最后矩阵中不存在两个四联通相邻的数字相等。一行或一列最多操作一次。问最小代价。

\(n\le 1000\)

我开始想错了,越转化问题越强,然后根本做不了。

\(x_i\) 表示第 \(i\) 行是否操作,\(y_i\) 同理,取值 \(\{0,1\}\)。每相邻两个数都会给它们所在的相邻两行/两列提供 0~1 个限制,形如 \(x_i\ne x_{i+1}\) 或者 \(x_i\le x_{i+1}\)\(x_i\ge x_{i+1}\) 等。不难发现行和列是独立的。

然后分别对行和列进行 dp 即可。时间复杂度 \(O(nm)\),瓶颈在于找到限制。

CF2096D Wonderful Lightbulbs

在平面直角坐标系的每一个整点上都有一个灯泡。初始时,有且仅有一个灯泡是点亮的。每次操作,你选择一个位置 \((x,y)\),然后将 \((x,y),(x,y+1),(x+1,y),(x+1,y-1)\) flip

给定最终局面(\(n\) 个点亮的点),问哪个灯泡是初始点亮的。保证有解。

\(n\le 2\times 10^5\),给定的点的坐标的绝对值 \(\le 10^8\)

首先题目给定的形状非常不规则。但是发现可以通过对坐标系的变换 \((x,y)\to (x,x+y)\) 使得其变为 \(2\times 2\) 的正方形。

由于值域很大,不难构造出一种操作次数 \(10^{13}\) 量级的最终局面,因此不能尝试构造方案。虽然但是,操作可以简化为对任意一个 \(2\times 2\) 的子矩阵(任选两行两列相交得到的 \(4\) 个点)进行操作,也不知道后面能不能做。

考察每次操作哪些量是不变的。注意到每行点数的奇偶性和每列点数的奇偶性不会改变。因此找出和为奇数的行以及和为奇数的列即可。

CF2096E Wonderful Teddy Bears

给定一个长为 \(n\) 的 01 串 \(s\)。每次你可以选择 \(s\) 一个长为 \(3\) 的连续子段,然后将其排序。问将整个串排好序的最少操作次数。

\(n\le 2\times 10^5\)

操作分为 \(4\) 种:

100 -> 001
110 -> 011
010 -> 001
101 -> 011

可以用逆序对来衡量数组的排序程度。注意到 \(1,2\) 可以减少两个逆序对,而 \(3,4\) 只能减少一个。我们希望最大化 \(1,2\) 操作的次数。

注意到 \(1,2\) 操作本质是交换了间隔一个数的两个数字,而 \(3,4\) 操作是交换了相邻两个数字。考察操作本质上改变了什么、没改变什么。由于 \(1,2\) 操作总是在对奇偶性相同的两个格子进行操作,因此不会改变奇(偶)格子内 \(1\)\(0\) 的数量。相反,\(3,4\) 操作一定会改变。

由于我们知道最终局面,因此也知道最终的时候奇数下标有多少个 \(0\)。由此我们能够算出 \(3,4\) 操作最少执行多少次,剩下的逆序对都靠 \(1,2\) 处理即可。

CF2096F Wonderful Impostors

有一个长为 \(n\)01\(s\)\(m\) 条命题,每条命题形如“区间 \([l,r]\) 全都是 \(0\)”或“区间 \([l,r]\) 存在一个 \(1\)”。

\(q\) 次询问,每次询问给定一个区间 \([l,r]\),问编号在 \([l,r]\) 的命题是否能同时满足。

\(n,m,q\le 2\times 10^5\)

只有一次询问的判定问题是好做的。将所有第一种命题在线段树上 -1,所有第二种命题就在线段树上查一下最大值即可。为了方便叙述,称第一种命题为操作一,第二种命题为操作二。

考虑一些处理区间询问的算法。题目询问的信息显然不好用线段树维护。考虑莫队,发现也不太好维护。注意到询问的区间具有单调性,可以预处理出每个右端点对应的最长区间。考虑双指针,它相比莫队可以保证只在合法时操作。

  • 新增一个操作二:直接在线段树上查一下即可;
  • 新增一个操作一:先找到当前操作所在的极长负数连续段,然后看看有没有操作二被包含在内;

如果不合法就 l++,直到合法之后再 r++

代码
#include<iostream>
#include<queue>
using namespace std;
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;

struct Qr {
    int tp, l, r;
} qr[N];

int T, n, m, q;
int ans[N];

namespace SegT1 {
    int mx[4 * N], tag[4 * N];
    inline int lc(int x) { return x << 1; }
    inline int rc(int x) { return x << 1 | 1; }
    inline void push_up(int p) { mx[p] = max(mx[lc(p)], mx[rc(p)]); }
    inline void move_tag(int p, int tg) { mx[p] += tg; tag[p] += tg; }
    inline void push_down(int p) { if(tag[p]) { move_tag(lc(p), tag[p]); move_tag(rc(p), tag[p]); tag[p] = 0; } }
    inline void clear() { for(int i = 0; i <= 4 * n; i++) mx[i] = tag[i] = 0; }
    void add(int p, int l, int r, int ql, int qr, int v) {
        if(ql <= l && r <= qr) { move_tag(p, v); return; }
        int mid = (l + r) >> 1; push_down(p);
        if(ql <= mid) add(lc(p), l, mid, ql, qr, v);
        if(mid < qr) add(rc(p), mid + 1, r, ql, qr, v);
        push_up(p);
    }
    int query_mx(int p, int l, int r, int ql, int qr) {
        if(ql <= l && r <= qr) return mx[p];
        int mid = (l + r) >> 1; push_down(p);
        if(qr <= mid) return query_mx(lc(p), l, mid, ql, qr);
        if(mid < ql) return query_mx(rc(p), mid + 1, r, ql, qr);
        return max(query_mx(lc(p), l, mid, ql, qr), query_mx(rc(p), mid + 1, r, ql, qr));
    }
    int query_l(int p, int l, int r, int qr) {
        if(l > qr) return 0;
        if(mx[p] < 0) return 0;
        if(l == r) return l;
        int mid = (l + r) >> 1; push_down(p);
        int res = query_l(rc(p), mid + 1, r, qr);
        if(res) return res;
        return query_l(lc(p), l, mid, qr);
    }
    int query_r(int p, int l, int r, int ql) {
        if(r < ql) return n + 1;
        if(mx[p] < 0) return n + 1;
        if(l == r) return l;
        int mid = (l + r) >> 1; push_down(p);
        int res = query_r(lc(p), l, mid, ql);
        if(res != n + 1) return res;
        return query_r(rc(p), mid + 1, r, ql);
    }
}

namespace SegT2 {
    int mx[4 * N];
    priority_queue<int> que[N], del[N];
    inline int lc(int x) { return x << 1; }
    inline int rc(int x) { return x << 1 | 1; }
    inline void push_up(int p) { mx[p] = max(mx[lc(p)], mx[rc(p)]); }
    inline void clear() {
        for(int i = 0; i <= 4 * n; i++) mx[i] = 0;
        for(int i = 0; i <= n + 5; i++) {
            while(!que[i].empty()) que[i].pop();
            while(!del[i].empty()) del[i].pop();
            que[i].push(-INF);
        }
    }
    void modify(int p, int l, int r, int q, int v) {
        if(l == r) { mx[p] = v; return; }
        int mid = (l + r) >> 1;
        if(q <= mid) modify(lc(p), l, mid, q, v);
        else modify(rc(p), mid + 1, r, q, v);
        push_up(p);
    }
    int query(int p, int l, int r, int ql, int qr) {
        if(ql <= l && r <= qr) return mx[p];
        int mid = (l + r) >> 1;
        if(qr <= mid) return query(lc(p), l, mid, ql, qr);
        if(mid < ql) return query(rc(p), mid + 1, r, ql, qr);
        return max(query(lc(p), l, mid, ql, qr), query(rc(p), mid + 1, r, ql, qr));
    }
    inline void insert(int l, int r) {
        que[r].push(l);
        while(!del[r].empty() && que[r].top() == del[r].top()) { que[r].pop(); del[r].pop(); }
        modify(1, 1, n, r, que[r].top());
    }
    inline void erase(int l, int r) {
        del[r].push(l);
        while(!del[r].empty() && que[r].top() == del[r].top()) { que[r].pop(); del[r].pop(); }
        modify(1, 1, n, r, que[r].top());
    }
    inline bool check(int l, int r) {
        return query(1, 1, n, l, r) >= l;
    }
}

bool check(Qr x) {
    int l = x.l, r = x.r;
    if(x.tp == 0) {
        int el = SegT1::query_l(1, 1, n, l - 1) + 1;
        int er = SegT1::query_r(1, 1, n, r + 1) - 1;
        return !SegT2::check(el, er);
    } else return SegT1::query_mx(1, 1, n, l, r) == 0;
}

void insert(Qr x) {
    int l = x.l, r = x.r;
    if(x.tp == 0) {
        SegT1::add(1, 1, n, l, r, -1);
    } else {
        SegT2::insert(l, r);
    }
}

void erase(Qr x) {
    int l = x.l, r = x.r;
    if(x.tp == 0) {
        SegT1::add(1, 1, n, l, r, 1);
    } else {
        SegT2::erase(l, r);
    }
}

void clear() {
    SegT1::clear();
    SegT2::clear();
}

int main() {

    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    cin >> T;
    while(T--) {
        cin >> n >> m;
        clear();
        for(int i = 1; i <= m; i++) {
            cin >> qr[i].tp >> qr[i].l >> qr[i].r;
        }
        for(int i = 1, j = 1; i <= m; i++) {
            while(j < i && !check(qr[i])) erase(qr[j++]);
            insert(qr[i]);
            ans[i] = j;
        }
        cin >> q;
        while(q--) {
            int l, r;
            cin >> l >> r;
            cout << (ans[r] <= l ? "yEs\n" : "nO\n");
        }
    }

    return 0;
}