• @codeforces



    @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' igcap Aj = phi),等价地就是 (Aj subset Ai);反过来也可以是 (Ai subset Aj)
    注意无解时,两行形成偏序关系(包含关系(subset))。这意味着如果整个局面无解,则所有行形成一个偏序链。
    而同时,这个偏序链是按照集合的大小排序的。

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

    时间复杂度 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
    ", x1 + 1, min(y1, y2) + 1, x2 + 1, max(y1, y2) + 1);
    		}
    	}
    }
    

    @details@

    update in 2019/09/28:感谢评论区。其实可以使用_Find_first()函数来找到第一个 1 所在位置。不过我懒得改了(

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

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

  • 相关阅读:
    android 之 ListView相关
    android 之 菜单
    android 之 Dialog
    android 之 View
    android 之 service
    android 之 Intent、broadcast
    Service Broadcast简单音乐播放功能
    剑指offer面试题43:n个筛子的点数
    C# LINQ语法
    C# Linq
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11562256.html
Copyright © 2020-2023  润新知