@codeforces - 1214G@ Feeling Good


@description@

给定一个 n*m 的 01 矩阵 A,一开始所有格子都为 0。

我们定义一个子矩阵 (x1, y1) - (x2, y2) 是好矩阵,当且仅当 A(x1, y1) = A(x2, y2);A(x2, y1) = A(x1, y2);A(x1, y1) ≠ A(x1, y2)。
其中满足 x1 < x2, y1 < y2。

现有 q 次修改,每次形式为 (a, l, r),表示将第 a 行的 l~r 个元素全部取反。

现需要在每次修改后判断是否有解。如果有,任意输出一组解。

Input
第一行三个整数 n, m, q (1≤n,m≤2000,1≤q≤500000)。
接下来 q 行每行三个整数 ai, li, ri,(1≤ai≤n, 1≤li≤ri≤m)。

Output
输出 q 行,第 i 行表示第 i 次操作后的任意一组好矩阵 x1, y1, x2, y2(1≤x1<x2≤n, 1≤y1<y2≤m)。
若无解输出 -1。

Examples
Input
2 2 6
1 1 1
2 2 2
2 1 1
1 2 2
2 2 2
1 1 1
Output
-1
1 1 2 2
-1
-1
-1
1 1 2 2

@solution@

神仙题。

我们记第 i 行形成的二进制数为 Ai;同时 Ai 也可表示一个集合:1 为元素存在,0 为元素不存在。
我们下文将不加区分,即 Ai 既可以使用集合运算也可以使用二进制运算。

首先考虑假如给定两行 Ai, Aj,怎么才能快速得到解。实际上就是找 Ai 中 0 对应 Aj 中 1;Aj 中 0 对应 Ai 中 1。
将 Ai 取反得到 Ai',则 Ai' 中 1 对应 Aj 中 1,将 Ai' 与 Aj 进行 & 运算即可;同理可以将 Aj' 与 Ai 进行 & 运算。
而以上操作不难使用 bitset 实现。

现在假如两行 Ai 与 Aj 之间没有解意味着什么?要么 Ai' 与 Aj 没有相交部分,即 \(Ai' \bigcap Aj = \phi\),等价地就是 \(Aj \subset Ai\);反过来也可以是 \(Ai \subset Aj\)
注意无解时,两行形成偏序关系(包含关系\(\subset\))。这意味着如果整个局面无解,则所有行形成一个偏序链。
而同时,这个偏序链是按照集合的大小排序的。

于是就可以设计出算法:我们把所有行用 bitset 维护修改操作。
对所有行对应的 bitset,按照其包含元素的多少,用 set 来进行维护。
每次在 set 里面插入删除时,维护 set 相邻两个元素是否为 包含关系\(\subset\):如果不是,说明这两行之间有解。
另开一个 set 存储这些行二元组。最后如果该 set 不为空则有解,按照上面的方法找出这样一个解(不过需要手写 bitset 支持 lowbit 查询)。

时间复杂度 O(p*(logn + m/64))。

@accepted code@

#include<cstdio>
#include<set>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int MAXN = 2000 + 5;
#define BITNUM 64
ull f[BITNUM];
struct bitset{
    #define SIZE 32
    ull a[SIZE], cnt;
    bitset() {
        for(int i=0;i<SIZE;i++) a[i] = 0;
    }
    void count() {
        cnt = 0;
        for(int i=0;i<SIZE;i++)
            cnt += __builtin_popcount(a[i]) + __builtin_popcount(a[i] >> 32);
    }
    friend bool operator < (const bitset &a, const bitset &b) {
        return a.cnt < b.cnt;
    }
    friend bitset operator &(const bitset &a, const bitset &b) {
        bitset c;
        for(int i=0;i<SIZE;i++)
            c.a[i] = a.a[i] & b.a[i];
        return c;
    }
    friend bitset operator ^(const bitset &a, const bitset &b) {
        bitset c;
        for(int i=0;i<SIZE;i++)
            c.a[i] = a.a[i] ^ b.a[i];
        return c;
    }
    friend bitset operator ~(const bitset &a) {
        bitset c;
        for(int i=0;i<SIZE;i++)
            c.a[i] = ~a.a[i];
        return c;
    }
    friend bool operator == (const bitset &a, const bitset &b) {
        for(int i=0;i<SIZE;i++)
            if( a.a[i] != b.a[i] )
                return false;
        return true;
    }
    friend bool operator != (const bitset &a, const bitset &b) {
        return !(a == b);
    }
    void set(ull k) {
        for(int i=0;i<SIZE;i++) {
            if( k < BITNUM ) {
                a[i] |= f[k];
                break;
            }
            else k -= BITNUM;
        }
    }
    ull bit() {
        for(int i=0;i<SIZE;i++)
            if( a[i] ) {
                int l = 0, r = BITNUM - 1;
                while( l < r ) {
                    int mid = (l + r + 1) >> 1;
                    if( f[mid] <= a[i] ) l = mid;
                    else r = mid - 1;
                }
                return l + i*BITNUM;
            }
        puts("error");
        exit(0);
    }
};
set<pair<bitset, int> >st1;
set<pair<bitset, int> >::iterator it1, it2, it3;
set<pair<int, int> >st2;
set<pair<int, int> >::iterator it;
bitset bts[MAXN], b[MAXN];
int n, m, q;
bool check(const bitset &a, const bitset &b) {
    if( (a & b) != a && (a & b) != b )
        return true;
    else return false;
}
void init() {
    f[0] = 1;
    for(int i=1;i<BITNUM;i++)
        f[i] = f[i-1]<<1;
}
int main() {
    init();
    scanf("%d%d%d", &n, &m, &q);
    for(int i=1;i<=m;i++)
        b[i] = b[i-1], b[i].set(i - 1), b[i].count();
    for(int i=0;i<n;i++)
        st1.insert(make_pair(bts[i], i));
    for(int i=1;i<=q;i++) {
        int a, l, r; scanf("%d%d%d", &a, &l, &r), a--;
        it1 = st1.find(make_pair(bts[a], a));
        if( it1 == st1.begin() )
            it2 = st1.end();
        else it2 = it1, it2--;
        it3 = it1, it3++;
        if( it2 != st1.end() ) {
            if( check(it2->first, it1->first) )
                st2.erase(make_pair(it2->second, a));
        }
        if( it3 != st1.end() ) {
            if( check(it1->first, it3->first) )
                st2.erase(make_pair(a, it3->second));
        }
        if( it2 != st1.end() && it3 != st1.end() ) {
            if( check(it2->first, it3->first) )
                st2.insert(make_pair(it2->second, it3->second));
        }
        st1.erase(make_pair(bts[a], a));
        bts[a] = bts[a] ^ b[r] ^ b[l - 1]; bts[a].count();
        st1.insert(make_pair(bts[a], a));
        it1 = st1.find(make_pair(bts[a], a));
        if( it1 == st1.begin() )
            it2 = st1.end();
        else it2 = it1, it2--;
        it3 = it1, it3++;
        if( it2 != st1.end() ) {
            if( check(it2->first, it1->first) )
                st2.insert(make_pair(it2->second, a));
        }
        if( it3 != st1.end() ) {
            if( check(it1->first, it3->first) )
                st2.insert(make_pair(a, it3->second));
        }
        if( it2 != st1.end() && it3 != st1.end() )
            if( check(it2->first, it3->first) )
                st2.erase(make_pair(it2->second, it3->second));
        if( st2.empty() ) {
            puts("-1");
        }
        else {
            pair<int, int> p = *st2.begin();
            int x1 = min(p.first, p.second), x2 = max(p.first, p.second);
            int y1 = (bts[p.first] & (~bts[p.second])).bit();
            int y2 = (bts[p.second] & (~bts[p.first])).bit();
            printf("%d %d %d %d\n", x1 + 1, min(y1, y2) + 1, x2 + 1, max(y1, y2) + 1);
        }
    }
}

@details@

在网上查了半天确认了 bitset 真的不能用 lowbit。

表示 __builtin_popcount 函数的参数只能是 long int,调了半天。
还因为这个函数 T 了几发。。。辣鸡玩意儿

转载于:https://www.cnblogs.com/Tiw-Air-OAO/p/11562256.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值