【Codeforces 1214G】Feeling Good

bitset

题目大意:

给定一个 01 矩阵,每次会翻转某行的一个子串。翻转完后要求找到一个子矩阵,其左上角为 $(x_1,y_1)$,右下角为 $(x_2,y_2)$,并且其对角相等,同侧不相等。输出任意方案或表示无解。

题解:

妙 bitset 题。

考虑对每行维护 bitset,就可以方便地进行取反操作。

考虑两行 $a,b$ 能够作为矩阵的上、下边界需要满足的条件,必须满足至少有一个位置,前者为 0 后者为 1,并且至少有一个位置,前者为 1 后者为 0。那么必须满足 $a\not\subset b$ 且 $b\not\subset a$。

而对于三个集合 $a,b,c$,如果 $a\subset b$ 且 $b\subset c$ 那么就有 $a\subset c$。

所以我们按照 bitset 中 1 的个数从小到大排序,然后判断一下相邻两个是否不互相包含就好了。

用 set 存 bitset 对应的行数,按照 1 的个数排序,再用一个 set 存哪些行可能作为答案。

然后询问的时候,随便从答案 set 里找两行,然后要查询列。

我们需要找两个位置,一个在 $a$ 里为 1,$b$ 里为 0,另一个反之。

可以通过位运算方便地搞出有哪些位满足条件。

至于要任意找一个是 1 的位的话,bitset 里有个函数叫做_Find_first,查找第一个 1 的位置,复杂度是 $O(\frac{n}{\omega})$ 的。

总时间复杂度 $O(q(\log n+\frac{m}{\omega}))$。

Code:

#include<iostream>
#include<set>
#include<bitset>
using namespace std;
const int N=2002;
typedef bitset<N>bt;
int n,m,q;
bt b[N],pre[N];int ct[N];
set<pair<int,int> >st,ok;
void erase(int cnt,int x){
    auto it=st.find(make_pair(cnt,x));
    auto it2=it;++it2;
    if(it2!=st.end())ok.erase(make_pair(it->second,it2->second));
    it2=it;
    if(it!=st.begin()){
        --it2;
        ok.erase(make_pair(it2->second,it->second));
        st.erase(it);
    }else{
        st.erase(it);
        return;
    }
    it=it2;
    ++it;
    if(it!=st.end()&&(b[it2->second]&b[it->second])!=b[it2->second])
    ok.insert(make_pair(it2->second,it->second));
}
void insert(int cnt,int x){
    auto it=st.lower_bound(make_pair(cnt,x));
    auto it2=it;
    if(it!=st.begin()){
        --it2;
        ok.erase(make_pair(it2->second,it->second));
    }
    it=st.insert(make_pair(cnt,x)).first;
    it2=it;++it2;
    if(it2!=st.end()&&(b[it->second]&b[it2->second])!=b[it->second])ok.insert(make_pair(it->second,it2->second));
    it2=it;
    if(it!=st.begin()){
        --it2;
        if((b[it->second]&b[it2->second])!=b[it2->second])
        ok.insert(make_pair(it2->second,it->second));
    }
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m>>q;
    for(int i=1;i<=n;++i)st.insert(make_pair(0,i));
    for(int i=1;i<=m;++i)
    pre[i]=pre[i-1],pre[i][i]=1;
    while(q--){
        int p,l,r;
        cin>>p>>l>>r;
        erase(ct[p],p);
        b[p]^=pre[r]^pre[l-1];
        ct[p]=b[p].count();
        insert(ct[p],p);
        if(ok.empty())cout<<"-1\n";else{
            int x1=ok.begin()->first,x2=ok.begin()->second;
            if(x1>x2)x1^=x2^=x1^=x2;
            int y_1=(b[x1]&(b[x1]^b[x2]))._Find_first();
            int y_2=(b[x2]&(b[x1]^b[x2]))._Find_first();
            if(y_1>y_2)y_1^=y_2^=y_1^=y_2;
            cout<<x1<<' '<<y_1<<' '<<x2<<' '<<y_2<<'\n';
        }
    }
    return 0;
}