読者です 読者をやめる 読者になる 読者になる

Lilliput Steps

小さな一歩から着実に. 数学やプログラミングのことを書きます.

JOI春合宿 2012-day3 fortune telling

データ構造 JOI

問題文 : 占い

解法 :
あるカードを奇数回ひっくり返せばそのカードは裏で, 偶数回ひっくり返せばそのカードは表である.

これは, カードをひっくり返す順序によらないので, y1 ≦ y ≦ y2 行 x1 ≦ x ≦ x2列のカード
をひっくり返す, という命令を
x1 ≦ x ≦ x2という区間を, y1でひっくり返し, y2 + 1でもとに戻す, というイベントとして扱う.

イベント化した後はyの順序でソートする. こうすることで, O(M)個のセグメント木を持たずに1個のセグメント木だけでひっくり返す操作を行うことが出来る.
O(M)回のひっくり返す操作は, セグメント木と用いて一回の操作をO(logM)時間で行うことが出来る.

ゆえに, このプログラムは, O(MlogM)時間で動作する.

コード :

#include <cstdio>
#include <map>
#include <algorithm>

typedef long long lint;

using namespace std;

struct Node {
	bool flip;
	lint val;
	Node(){
		flip = false;
		val = 0;
	}
};

struct Event {
	int l, r, y;
	Event(){
		l = r = y = 0;
	}
	Event(int l, int r, int y) : l(l), r(r), y(y) {}
};

Node seg[1 << 19];
lint cmp[200000];
int sz;

void init(int n)
{
	sz = 1;
	while (sz < n){
		sz *= 2;
	}
}

inline void evaluate(int k, int a, int b)
{
	if (seg[k].flip) seg[k].val = (cmp[b] - cmp[a]) - seg[k].val;
	
	if (k < sz - 1){
		seg[k * 2 + 1].flip ^= seg[k].flip;
		seg[k * 2 + 2].flip ^= seg[k].flip;
	}
	seg[k].flip = false;
}

void _update(int k)
{
	seg[k].val = seg[k * 2 + 1].val + seg[k * 2 + 2].val;
}

void update(int a, int b, int k = 0, int l = 0, int r = sz)
{
	evaluate(k, l, r);
	
	if (b <= l || r <= a) return;
	
	if (a <= l && r <= b){
		seg[k].flip ^= 1;
		evaluate(k, l, r);
		return;
	}
	update(a, b, k * 2 + 1, l, (l + r) / 2);
	update(a, b, k * 2 + 2, (l + r) / 2, r);
	_update(k);
	
	return;
}

lint getSum(int a, int b, int k = 0, int l = 0, int r = sz)
{
	evaluate(k, l, r);
	
	if (b <= l || r <= a) return (0);
	
	if (a <= l && r <= b){
		evaluate(k, l, r);
		return (seg[k].val);
	}
	
	lint lch = getSum(a, b, k * 2 + 1, l, (l + r) / 2);
	lint rch = getSum(a, b, k * 2 + 2, (l + r) / 2, r);
	_update(k);
	
	return (lch + rch);
}

bool operator < (const Event &a, const Event &b)
{
	return (a.y < b.y);
}


int x1[100000], x2[100000], y1[100000], y2[100000];
int xs[200000];
Event ev[200000];

int main()
{
	int m, n, k;
	int idx;
	map<int, int> mp;
	
	scanf("%d %d %d", &m, &n, &k);
	
	for (int i = 0; i < k; i++){
		scanf("%d %d %d %d", y1 + i, y2 + i, x1 + i, x2 + i);
		x1[i]--; y1[i]--;
		xs[i * 2] = x1[i];
		xs[i * 2 + 1] = x2[i];
	}
	
	sort(xs, xs + 2 * k);
	mp[xs[0]] = idx = 0;
	cmp[0] = xs[0];
	for (int i = 1; i < k * 2; i++){
		if (xs[i] != xs[i - 1]){
			mp[xs[i]] = ++idx;
			cmp[idx] = xs[i];
		}
	}
	
	for (int i = 0; i < k; i++){
		ev[i * 2] = Event(mp[x1[i]], mp[x2[i]], y1[i]);
		ev[i * 2 + 1] = Event(mp[x1[i]], mp[x2[i]], y2[i]);
	}
	
	sort(ev, ev + k * 2);
	
	init(++idx);
	
	lint ret = 0;
	update(ev[0].l, ev[0].r);
	for (int i = 1; i < k * 2; i++){
		ret += (ev[i].y - ev[i - 1].y) * getSum(0, idx);
		update(ev[i].l, ev[i].r);
	}
	
	printf("%lld\n", (lint)m * n - ret);
	
	return (0);
}