@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 查询 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\n", 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 了几发。。。辣鸡玩意儿

@codeforces - 1214G@ Feeling Good的更多相关文章

  1. python爬虫学习(5) —— 扒一下codeforces题面

    上一次我们拿学校的URP做了个小小的demo.... 其实我们还可以把每个学生的证件照爬下来做成一个证件照校花校草评比 另外也可以写一个物理实验自动选课... 但是出于多种原因,,还是绕开这些敏感话题 ...

  2. 【Codeforces 738D】Sea Battle(贪心)

    http://codeforces.com/contest/738/problem/D Galya is playing one-dimensional Sea Battle on a 1 × n g ...

  3. 【Codeforces 738C】Road to Cinema

    http://codeforces.com/contest/738/problem/C Vasya is currently at a car rental service, and he wants ...

  4. 【Codeforces 738A】Interview with Oleg

    http://codeforces.com/contest/738/problem/A Polycarp has interviewed Oleg and has written the interv ...

  5. CodeForces - 662A Gambling Nim

    http://codeforces.com/problemset/problem/662/A 题目大意: 给定n(n <= 500000)张卡片,每张卡片的两个面都写有数字,每个面都有0.5的概 ...

  6. CodeForces - 274B Zero Tree

    http://codeforces.com/problemset/problem/274/B 题目大意: 给定你一颗树,每个点上有权值. 现在你每次取出这颗树的一颗子树(即点集和边集均是原图的子集的连 ...

  7. CodeForces - 261B Maxim and Restaurant

    http://codeforces.com/problemset/problem/261/B 题目大意:给定n个数a1-an(n<=50,ai<=50),随机打乱后,记Si=a1+a2+a ...

  8. CodeForces - 696B Puzzles

    http://codeforces.com/problemset/problem/696/B 题目大意: 这是一颗有n个点的树,你从根开始游走,每当你第一次到达一个点时,把这个点的权记为(你已经到过不 ...

  9. CodeForces - 148D Bag of mice

    http://codeforces.com/problemset/problem/148/D 题目大意: 原来袋子里有w只白鼠和b只黑鼠 龙和王妃轮流从袋子里抓老鼠.谁先抓到白色老鼠谁就赢. 王妃每次 ...

随机推荐

  1. leetcode 563 - 653

    563. Binary Tree Tilt Input: 1 / \ 2 3 Output: 1 Explanation: Tilt of node 2 : 0 Tilt of node 3 : 0 ...

  2. 深入了解MVC(转)

    MVC无人不知,可很多程序员对MVC的概念的理解似乎有误,换言之他们一直在错用MVC,尽管即使如此软件也能被写出来,然而软件内部代码的组织方式却是不科学的,这会影响到软件的可维护性.可移植性,代码的可 ...

  3. day18 17.c3p0连接池使用

    连接池时间长不用空闲着,dbcp是不回收的,性能可能有些问题.c3p0是可以自动回收.实际开发中c3p的生产力比dbcp强,性能上更强. package cn.itcast.datasource; i ...

  4. TP3.2.x判断手机端访问并设置默认访问模块的方法 - ThinkPHP框架

    手机端访问时调用Wap手机模块,实现在手机端访问时展示出手机网站,无需跳转域名首先我们在./Application/Common/Conf/ 目录下建立两个公共配置文件:config.php 和con ...

  5. android rsa 示例

    1.参考资料 1.1 android的Cipher官方文档 https://developer.android.com/reference/javax/crypto/Cipher 其中 构造Ciphe ...

  6. vue使用填坑之:model和v-model的区别

    v-model通常用于input的双向数据绑定 <input v-model="parentMsg">,也可以实现子组件到父组件数据的双向数据绑定:首先说说v-mode ...

  7. cmd 运行 python

    ①cmd 进入行命令: ②输入 “python” + “空格”,即 ”python “:将已经写好的脚本文件拖拽到当前光标位置,然后敲回车运行即可.

  8. ASP.NET CORE使用MailKit的一个故障点分析

    ASP.NET CORE下有需要发邮件的需求,但是原来framework下的 system.net.mail,没有实现smtpclient的功能(当时看是没有,说是准备并入.net core来着),所 ...

  9. 如何在CentOS 7 / Fedora 31/30/29上安装ELK Stack

    原文地址:https://computingforgeeks.com/how-to-install-elk-stack-on-centos-fedora/ 原作者: Josphat Mutai 译者: ...

  10. Leetcode849.Maximize Distance to Closest Person到最近的人的最大距离

    在一排座位( seats)中,1 代表有人坐在座位上,0 代表座位上是空的. 至少有一个空座位,且至少有一人坐在座位上. 亚历克斯希望坐在一个能够使他与离他最近的人之间的距离达到最大化的座位上. 返回 ...