[loj2864] [ioi2018 d1t2] seats - 線段樹

傳送門:https://loj.ac/problem/2864

作死嘗試這道全世界只有1人ac的神題。

先%一波dsgsjk,這位吊打全世界的julao可能是全世界第二個做出這題的現役oi選手。

題目大意:給定一個n*m的矩陣,矩陣中不重不漏地分佈着0~n*m-1的元素。稱一個大小爲s的連續子矩陣是好的當且僅當其中不重不漏地出現了0~s-1的元素。每次交換矩陣中的兩個元素,查詢當前有多少個好的子矩陣。強制在線。

這題容易給人一種奇技淫巧的數據結構的感覺。那麼到底該用什麼數據結構,維護什麼信息?

我們發現我們可以通過維護前i個元素在矩陣中位置分佈相關的信息,來檢驗前i個元素是否能組成合法子矩陣。

開一棵線段樹,其下標爲i的位置維護的是前i個元素的信息。

那麼問題來了,我們需要維護什麼信息,能夠支持修改和快速計算答案?

這確實是一個棘手的問題。估計現場應該很多人都跪在這一步了。

我一開始的想法是,維護前i個元素所組成的圖形的周長。後來發現據此並不能直接計算答案(一個大小一定的矩形其周長也可能有多種可能),遂放棄。

後來我想到一種“看似可行”的思路:維護前i個元素的x、y座標的最小值和最大值,這樣前i個元素組成合法子矩形當且僅當(xmax - xmin + 1) * (ymax - ymin + 1) = s。

這樣我們將每個位置的值設爲(xmax - xmin + 1) * (ymax - ymin + 1) - i,這個數一定非負,當且僅當它爲0時答案合法。

然而這個想法在修改時遇到了麻煩:交換兩個元素,可能導致一個區間的max和min值發生大規模變化,尤其是當把原來的最值移動到後面時,可能造成區間內最值的多次改變,我們可能面臨每次操作O(n)次修改。

目前我還沒有想到合適的方法來維護這個東西,如果有大神會維護的話麻煩在評論中留言謝謝。

那怎麼辦?就要祭出dsgsjk大神的天才級思路了:通過維護“特殊點”的信息來實現快速修改和判斷合法性的目的!

爲了方便,我們現在對於一個位置i,將前i個點染成黑色,後面的點爲白色。

我們維護這麼兩個東西:

1、所有的黑點中,有多少個點的左側和上側都不挨着黑點(可能是白點或邊緣)。

2、所有的白點中,有多少個點的周圍四連通地挨着超過2個黑點。

這兩個信息能說明什麼?

首先,容易證明所有的合法子矩陣都滿足:第一個信息爲1,第二個信息爲0。接下來我們證明:一個非法的圖形不可能同時滿足以上兩個條件。

容易看出,對於每個黑點組成的四連通塊,我們總能選出其最左上的點(至少一個,也可能一個連通塊中有多個合法點),使得它的左邊和上邊都不是黑點。也就是說,黑色連通塊數量<=信息1。

因此,一個有>=2個連通塊的圖形不可能滿足信息1數量爲1。

再來考慮一個連通塊但不是矩形的圖形。容易發現這個圖形一定存在如下的“拐角”:

xxoo

xxoo

xxxx

xxxx

x表示黑點,o表示白點,2行3列那個白點就是“拐角”。而在拐角處的白點滿足信息2。

也就是說,存在拐角的圖形不可能滿足信息2數量爲0。

那麼同時滿足1和2的圖形,就只有“一個連通塊且不存在拐角”的圖形了——也就是矩形!

這樣我們就證明了條件的充要性。那麼怎麼維護呢?

我們直接維護兩個信息的和(這個和顯然是正整數),然後統計有多少個位置答案爲1。

交換兩個位置時,我們可以把預製相鄰的所有位置都拿出來(最多不超過10個,注意去重),然後暴力地對每個位置,先扣去原來對信息的貢獻,再加上操作後對信息的貢獻。對應在線段樹上就是O(1)次區間修改。

至於查詢區間有多少個1,我們可以用類似維護矩形面積並的方式,記錄區間最小值和最小值的個數。

額……或許這道令全世界oi高手望而卻步的難題,被dsgsjk做成了一道九省難度的數據結構?

//以下代碼根據ioi提交格式編寫

#include<bits/stdc++.h>
using namespace std;
#include"seats.h"
int n,m,p,k,jz[1000010],vl[1000010];
struct wz{
	int x,y;
}a[1000010];
int t[4000010],s[4000010],c[4000010];
#define ls q << 1
#define rs q << 1 | 1
#define ln ls,l,mid
#define rn rs,mid + 1,r
#define md int mid = l + r >> 1
void build(int q,int l,int r){
	if(l == r){
		t[q] = vl[l];s[q] = 1;return;
	}
	md;
	build(ln);build(rn);
	t[q] = min(t[ls],t[rs]);
	s[q] = 0;if(t[q] == t[ls]) s[q] += s[ls];if(t[q] == t[rs]) s[q] += s[rs];
}
inline void ps(int q){
	t[ls] += c[q];c[ls] += c[q];
	t[rs] += c[q];c[rs] += c[q];
	c[q] = 0;
}
void ins(int q,int l,int r,int al,int ar,int x){
	if(al > ar) return;
	if(l >= al && r <= ar){
		t[q] += x;c[q] += x;return;
	}
	ps(q);md;
	if(mid >= al) ins(ln,al,ar,x);
	if(mid < ar) ins(rn,al,ar,x);
	t[q] = min(t[ls],t[rs]);
	s[q] = 0;if(t[q] == t[ls]) s[q] += s[ls];if(t[q] == t[rs]) s[q] += s[rs];
}
int dx[4] = {0,-1,0,1},dy[4] = {-1,0,1,0};
#define mt(x,y) jz[(x - 1) * m + (y)]
#define lx(x) (x + dx[0])
#define ux(x) (x + dx[1])
#define fx(x,i) (x + dx[i])
#define fy(x,i) (x + dy[i])
#define ly(x) (x + dy[0])
#define uy(x) (x + dy[1])
#define chk(x,y) (x >= 1 && x <= n && y >= 1 && y <= m)
#define ax(q) a[q].x
#define ay(q) a[q].y
#define qwq(x,y) mt(fx(ax(x),y),fy(ay(x),y))
inline int ed(int q){
	int mn = p + 1;
	if(chk(lx(ax(q)),ly(ay(q)))) mn = min(mn,qwq(q,0));
	if(chk(ux(ax(q)),uy(ay(q)))) mn = min(mn,qwq(q,1));
	return mn;
}
inline int bg(int q){
	int mn1 = p + 1,mn2 = p + 1;
	for(int i = 0;i < 4;++i) if(chk(fx(ax(q),i),fy(ay(q),i))){
		if(qwq(q,i) < mn1) mn2 = mn1,mn1 = qwq(q,i);
		else if(qwq(q,i) < mn2) mn2 = qwq(q,i);
	} 
	return mn2;
}
int st[15],ft;
void give_initial_chart(int h,int c,vector<int> x,vector<int> y){
	n = h;m = c;p = n * m;
	for(int i = 1;i <= p;++i){
		a[i].x = x[i - 1] + 1;a[i].y = y[i - 1] + 1;mt(ax(i),ay(i)) = i;
	}
	for(int i = 1;i <= p;++i){
		vl[i] = vl[i - 1];
		if(bg(i) < i) --vl[i];
		if(ed(i) > i) ++vl[i];
		for(int j = 0;j < 4;++j) if(chk(fx(ax(i),j),fy(ay(i),j))){
			if(qwq(i,j) < i && ed(qwq(i,j)) == i) --vl[i];
			else if(qwq(i,j) > i && bg(qwq(i,j)) == i) ++vl[i];
		}
	}
	build(1,1,p);
}
int swap_seats(int u,int v){
	++u;++v;if(u > v) swap(u,v);
	if(u == v) return s[1];
	ft = 0;
	st[++ft] = u;st[++ft] = v;
	for(int j = 0;j < 4;++j) if(chk(fx(ax(u),j),fy(ay(u),j))) st[++ft] = qwq(u,j);
	for(int j = 0;j < 4;++j) if(chk(fx(ax(v),j),fy(ay(v),j))) st[++ft] = qwq(v,j);
	sort(st + 1,st + ft + 1);
	for(int j = 1;j <= ft;++j) if(st[j] != st[j - 1]){
		if(bg(st[j]) < st[j]) ins(1,1,p,max(bg(st[j]),u),min(st[j],v) - 1,-1);
		if(ed(st[j]) > st[j]) ins(1,1,p,max(st[j],u),min(ed(st[j]),v) - 1,-1);
	}
	swap(mt(ax(u),ay(u)),mt(ax(v),ay(v)));swap(a[u],a[v]);
	for(int j = 1;j <= ft;++j) if(st[j] != st[j - 1]){
		if(bg(st[j]) < st[j]) ins(1,1,p,max(bg(st[j]),u),min(st[j],v) - 1,1);
		if(ed(st[j]) > st[j]) ins(1,1,p,max(st[j],u),min(ed(st[j]),v) - 1,1);
	}
	return s[1];
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章