2018-2019 ACM—ICPC SEERC 題解

2018 - 2019 SEERC 題解

比賽發出來太新了,網上根本就搜不到題解,補題補的太難受了.
在這裏分享一篇我自己寫的題解,也方便別人補題.

題目鏈接

http://codeforces.com/gym/101964/attachments/download/7814/seerc-2018.pdf


A.Numbers

不留坑,這題不會.


B.Broken Watch

題解

先考慮三個針長度各不一樣的情況.

注意到需要對nn分奇偶性進行討論.

  1. nn爲偶數的時候,固定11號針的位置,枚舉22號針的位置,那麼33號針的位置必然在1,21,2號針的反向延長線形成的扇形區域內(可與邊界重合).
    並注意到當1,21,2號針反向的時候,33號針有n2n-2種取法.
    可以得到公式n(1+2+...+n2+n2)n(1+2+...+\frac{n}{2} + n-2).
  2. nn爲奇數的時候,固定11號針的位置枚舉22號針的位置,那麼33號針的位置必然在1,21,2號針反向延長線之間(不能與邊界重合).
    可以得到公式n(1+2+...+n2)n(1+2+...+\lfloor \frac{n}{2} \rfloor)

而當三根針有兩根相同時,需要對答案除以2!2!,當三根全都相同時,需要對答案除以3!3!,這題就結束了.不明白爲什麼這個題比CC題過得人少?

代碼

org = input().split(" ")

a = int(org[0])
b = int(org[1])
c = int(org[2])
n = int(org[3])

len = 1

if a == b and b == c:
    len = 6
elif a == b or b == c or a == c:
    len = 2

mod = 2**64

ans = 0
if n % 2 == 0:
    ans = (n*((n//2-1)*(n//2+2)+(n-2))//len )% mod
else:
    ans = (n*(n//2)*(n//2+1)//len) % mod

print(ans)

C.Tree

題解

訓練的時候想了一種樹dp的做法,不太好調,幸好最後還是A掉了.賽後翻比別人代碼發現還有一種很巧妙地方法,即枚舉樹的直徑.兩種方法我都簡略說一下.

方法一.樹dp

二分最大距離MM,然後樹dpdpcheckcheck可行性.

定義dp[i][j]dp[i][j]表示以ii爲根的子樹,選出來的黑點中距ii節點距離不會超過jj,所能選出最多的黑點個數.
並記lim=MIN{M1j,j1}lim = MIN\{M-1-j,j-1\}
那麼轉移就是:
假設v1,v2,...,vmv_1,v_2,...,v_mii的兒子節點.

  1. MIN1pm{dp[vp][j1]+q!=pdp[vq][lim]}dp[i][j]MIN_{1\le p \le m}\{dp[v_p][j-1] + \sum_{q != p}dp[v_q][lim]\} \rightarrow dp[i][j]

  2. dp[i][j1]dp[i][j]dp[i][j-1] \rightarrow dp[i][j]

最後只要看dp[1][M]kdp[1][M] \ge k.

時間複雜度?
O(n3log(n))O(n^3log(n))

方法二.枚舉樹的直徑

先預處理出樹上兩點之間的距離(使用Floyd算法即可).

注意到將黑點取出之後會形成一顆虛樹,並且兩兩之間最長的距離就是

然後我們考慮枚舉這顆虛樹的直徑,假設是(i,j)(i,j),然後再枚舉黑點,黑點進到虛樹中一定不能使直徑邊長.所以就要要求dis[i][k]dis[i][j]&&dis[k][j]dis[i][j]dis[i][k] \le dis[i][j] \&\& dis[k][j] \le dis[i][j].

時間複雜度O(n3)O(n^3)

這個方法簡單多了.

注意:不需要建虛樹,說虛樹主要是好描述.

代碼

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(int i = a;i <= b;++i)

using namespace std;

const int maxn = 105;
int n,m,val[maxn],dp[maxn][maxn];
vector<int> G[maxn];

void dfs(int cur,int pre,int mid){
	for(int nx : G[cur]) if(nx != pre) dfs(nx,cur,mid);

	if(val[cur] == 1) dp[cur][0] = 1;
	for(int i = 1;i <= mid;i++){
		int limit = min(mid-1-i,i-1), sum = 0;
		if(limit >= 0) for(int nx : G[cur]) if(nx != pre){
			sum += dp[nx][limit];
		}
		dp[cur][i] = max(dp[cur][i],dp[cur][i-1]);
		for(int nx : G[cur]){
			if(nx == pre) continue;
			int tmp = dp[nx][i-1];
			if(limit >= 0) tmp += sum - dp[nx][limit];
			dp[cur][i] = max(dp[cur][i], val[cur]+tmp);
		}
	}
}
bool check(int mid){
	memset(dp,0,sizeof(dp));
	dfs(1,-1,mid);
	int f = 0;
	for(int i = 1;i <= n;++i)
		f |= dp[i][mid] >= m;
	return f;
}

int main(){
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i = 1;i <= n;i++) cin>>val[i];
	for(int i = 0;i < n-1;i++){
		int u,v;
		cin>>u>>v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	int l = 0, r = n;
	while(r - l > 1){
		int mid = (l + r) >> 1;
		if(check(mid)) r = mid;
		else l = mid + 1;
	}
	if(check(l)) cout<<l<<endl;
	else cout<<r<<endl;
	
	return 0;
}

D.Space Station

題解

一道很神奇的題,需要先猜出結論,然後再進行樹上揹包.思路弄清楚了實現起來很簡單.

簡述一下題意:一顆nn個結點的帶權樹,要求從11號點出發,遍歷所有的,然後回到11號點.途中可以最多使用mm次技能,技能花費時間爲kk,功能是在任意兩點中穿越.求最小代價.

我們先來發現一些規律性的東西.

  1. 注意到一顆子樹如果子樹中包含的技能出發點和技能到達點共pp個,如果pp是偶數的時候,那麼這顆子樹根節點的父親邊一定要走兩次(可以畫圖幫助理解一下)
  2. 而如果pp是奇數的話,那麼子樹根節點的父親邊只需要走一次就好了.(畫圖幫助理解)

這就很簡單了.

定義dp[i][j]dp[i][j]表示以ii爲根的子樹,其子樹中奇點個數爲jj,從ii點出發遍歷完ii的子樹再回到ii點,所需要的最小代價.

那麼轉移方程就是

p1+p2+...+pm=j(dp[vi][pi]+(![pi&amp;1]+1)ci)dp[u][j]\sum_{p_1+p_2+...+p_m=j}(dp[v_i][p_i]+(![p_i\&amp;1]+1)*c_i) \rightarrow dp[u][j]

p1+p2+...+pm=j(dp[vi][pi]+(![pi&amp;1]+1)ci)dp[u][j+1]\sum_{p_1+p_2+...+p_m=j}(dp[v_i][p_i]+(![p_i\&amp;1]+1)*c_i) \rightarrow dp[u][j+1]

代碼

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(int i = a;i <= b;++i)
typedef std::pair<int,int> pii;
const int N = 1007; 
std::vector<pii> edge[N];
int dp[N][N],sz[N],tmp[N];
int n,m,k,T;
void upd(int &x,int y){
	if(y < x) x = y;
}
void dfs(int u,int fa) {
	sz[u] = 1;
	for(pii p : edge[u]) {
		int v = p.first,c = p.second;
		if(v == fa) continue;
		dfs(v,u);
		memset(tmp,0x3f,sizeof(tmp));
		for(int i = 0;i <= sz[u];++i) {
			for(int j = 0;j <= sz[v];++j) {
				if(i + j > 2*m) continue;
				if(j % 2 != 0) {
					upd(tmp[i+j],dp[u][i] + dp[v][j] + c);
					upd(tmp[i+j+1],dp[u][i] + dp[v][j] + c);
				}
				else {
					upd(tmp[i+j],dp[u][i] + dp[v][j] + 2*c);
					upd(tmp[i+j+1],dp[u][i] + dp[v][j] + 2*c);
				}
			}
		}
		sz[u] += sz[v];
		for(int i = 0;i <= sz[u];++i)
			dp[u][i] = tmp[i];
	}
}

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cin >> T;
	while(T--) {
		std::cin >> n >> m >> k;
		memset(dp,0,sizeof(dp));
		rep(i,1,n) 
			edge[i].clear();
		rep(i,1,n-1) {
			int u,v,c;
			std::cin >> u >> v >> c;
			edge[u].push_back((pii){v,c});
			edge[v].push_back((pii){u,c});
		}
		dfs(1,0);
		int ans = 0x3f3f3f3f;
		for(int i = 0;i <= std::min(2*m,n);i += 2) 
			upd(ans,dp[1][i]+(i/2*k));
		std::cout << ans << std::endl;
	}
	return 0;
}

E.Fisherman

題解

首先我們將釣魚的人按照xx座標排個序,同時也記錄一下他在答案裏的順序以便逆映射回去。
然後考慮每一條魚能被哪些漁夫釣到,也就是考慮貢獻。我們能夠很簡單地知道,一條魚能夠被釣到的地方在xx軸上是一段連續的區間。當魚的y座標大於所給的線的長度時釣不到,否則記魚線長度大於yy座標的長度ly=dll - y = dl,能夠釣到魚區間即爲[xdl,x+dl][x-dl, x+dl]。於是lower_bound和upper _bound找一下漁夫裏面所對應的區間,然後區間加單點查詢。區間加單點查詢這個操作在代碼裏是使用了差分數組來維護的,最後算完之後把差分數組還原即可得到答案。


F.Min Max Convert

題解

大致的題意是給你兩個長爲nn的數列A,BA,B,然後你每次可以選擇一段區間將區間覆蓋成它的最大值或者是覆蓋成它的最小值.要求輸出一個長不超過2n2n的方案,將AA數列變成BB數列.

很明顯的構造題.

我們首先要找一下規律以發現一些結論.

  1. 一個區間最多通過兩次操作可以將區間覆蓋爲區間中任意一個數.
    vv在區間邊界上時,例如v=a[l]v = a[l],將[l,r][l,r]覆蓋爲vv的方法是:判斷a[l]a[l]a[l+1]a[l+1]的大小關係,如果a[l]&gt;a[l+1]a[l] &gt; a[l+1],那麼就將[l+1,r][l+1,r]取最小值,再將[l,r][l,r]取最大值.
    vv[l,r][l,r]中間時,可以將區間分成兩段,分別操作.

  2. 對於BB數列的每個數,在AA數列中都應該有一個數與它對應.並且這些對應關係不交叉.
    比如:
    A:5,3,1,2,2,4,7,6,8,6A:5,3,1,2,2,4,7,6,8,6
    B:1,1,2,4,4,7,7,8,8,8B:1,1,2,4,4,7,7,8,8,8
    那麼對應關係是這樣的:

在這裏插入圖片描述
好難講,直接看我代碼吧.

代碼

#include <iostream>
#include <algorithm>
#include <cstring>
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(int i = a;i <= b;++i)

const int N = 100007;
int A[N],B[N],Loc[N];
char C[N<<1];int L[N<<1],R[N<<1];
int tot;
struct E{
	int b,e,x;
}es[N];
int etot = 0;
int n;
void Do(char c,int l,int r) {
	C[tot] = c;L[tot] = l;R[tot] = r;
	++tot;
}
int main() {
	scanf("%d",&n);
	rep(i,1,n) 
		scanf("%d",&A[i]);
	rep(i,1,n) 
		scanf("%d",&B[i]);
	int pos = 1;
	rep(i,1,n) {
		while(pos <= n && A[pos] != B[i]) pos ++;
		if(pos == n+1) return 0*puts("-1");
		Loc[i] = pos;	
	}
	int p = 1;
	rep(i,1,n) {
		if(Loc[i] != Loc[i+1]) {
			es[etot++] = (E){p,i,Loc[i]};
			p = i+1;
		}
	}
	for(int i = etot-1;i >= 0;--i) {
		if(es[i].x < es[i].b) {
			if(A[es[i].x] >= A[es[i].x+1]) {
				Do('m',es[i].x+1,es[i].e);
				Do('M',es[i].x,es[i].e);
			}
			if(A[es[i].x] < A[es[i].x+1]) {
				Do('M',es[i].x+1,es[i].e);
				Do('m',es[i].x,es[i].e);
			}
		}
	}
	rep(i,0,etot-1) {
		if(es[i].x > es[i].e) {
			if(A[es[i].x] <= A[es[i].x-1]) {
				Do('M',es[i].b,es[i].x-1);
				Do('m',es[i].b,es[i].x);
			}	
			if(A[es[i].x] > A[es[i].x-1]) {
				Do('m',es[i].b,es[i].x-1);
				Do('M',es[i].b,es[i].x);
			}
		}
	}
	rep(i,0,etot-1) {
		if(es[i].b <= es[i].x && es[i].x <= es[i].e) {
			if(es[i].x > es[i].b && A[es[i].x] >= A[es[i].x-1]) {
				Do('m',es[i].b,es[i].x-1);
				Do('M',es[i].b,es[i].x);
			}
			if(es[i].x > es[i].b && A[es[i].x] < A[es[i].x-1]) {
				Do('M',es[i].b,es[i].x-1);
				Do('m',es[i].b,es[i].x);
			}
			if(es[i].x < es[i].e && A[es[i].x] >= A[es[i].x+1]) {
				Do('m',es[i].x+1,es[i].e);
				Do('M',es[i].x,es[i].e);
			}
			if(es[i].x < es[i].e && A[es[i].x] < A[es[i].x+1]) {
				Do('M',es[i].x+1,es[i].e);
				Do('m',es[i].x,es[i].e);
			}
		}
	}
	std::cout << tot << std::endl;
	rep(i,0,tot-1) {
		printf("%c %d %d\n",C[i],L[i],R[i]);
	}
	return 0;
}

G.Matrix Queries

題解

隊友LNCLNC寫的.

結論:我們稱依題目給出的公式計算矩陣AA的得分時遞歸處理到的矩陣均稱爲AA的“子矩陣”,記這些矩陣中有nn個爲不“純色”的矩陣,則矩陣A的得分爲4n+14n+1

證明:
用數學歸納法:當AA1×11\times1的矩陣時顯然結論成立。
假設A爲2k×2k2^k\times2^k的矩陣時結論成立,現證A爲2k+1×2k+12^{k+1}\times2^{k+1}矩陣時結論仍成立:

  1. AA純色,則其得分爲1,結論成立;
  2. AA不純色,記其四個子矩陣的不純色的子矩陣個數分別爲n1n_1n2n_2n3n_3n4n_4,則由假設它們的得分分別爲4n1+14n_1+14n2+14n_2+14n3+14n_3+14n4+14n_4+1,A中不純色的子矩陣個數爲n1+n2+n3+n4+1n_1+n_2+n_3+n_4+1,由定義A的得分s=5+(n1+n2+n3+n4)=4(n1+n2+n3+n4+1)+1s=5 +(n_1+n_2+n_3+n_4)=4(n_1+n_2+n_3+n_4+1)+1,結論成立。

綜上結論得證。

考慮到一個矩陣爲純色則這個矩陣的每行需修改相同奇偶次且每列也需修改相同奇偶次,統計有多少合法位置的連續2k2^k行與列修改的奇偶次數相同,相乘結果即2k×2k2^k\times2^k大小矩陣中純色的個數,求反面即可。這些區間形成了線段樹的結構,用線段樹維護即可。

代碼

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = (1<<20)+10;
ll seg[2][maxn*4],ans[2][21];

void modify(int id,int o,int l,int r,int deep,int x){
	if(l == r){
		seg[id][o] ^= 1;
		return;
	}
	int mid = (l + r) >> 1;
	if(x <= mid) modify(id,o<<1,l,mid,deep+1,x);
	else modify(id,o<<1|1,mid+1,r,deep+1,x);
	if(seg[id][o] != -1) ans[id][deep]--;
	if(seg[id][o<<1] == seg[id][o<<1|1]) seg[id][o] = seg[id][o<<1];
	else seg[id][o] = -1;
	if(seg[id][o] != -1) ans[id][deep]++;
}
inline void read(int& x){
	char ch = getchar();
	x = 0;
	for(;ch < '0' || ch > '9';ch = getchar());
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x*10+(ch-'0');
}
inline void write(ll x){
	char ch = x%10+'0';
	if(x >= 10) write(x/10);
	putchar(ch);
}

int main(){
	ios::sync_with_stdio(false);
	int n,m,sz;
	ll sum = 0;
	read(sz);read(m);
	n = (1 << sz);
	for(int i = 0;i < sz;i++){
		ans[0][i+1] = ans[1][i+1] = (1 << i);
		sum += (1ll << (i * 2)); 
	}
	for(int i = 0;i < m;i++){
		int id, x;
		read(id);read(x);
		modify(id,1,1,n,1,x);
		ll tmp = 0;
		for(int i = 1;i <= sz;i++){
			tmp += 1ll*ans[0][i] * ans[1][i];
		}
		write(4ll * (sum - tmp) + 1);puts("");
	}
	return 0;
}

H.Modern Djinn

題解

留坑.


I.Inversion

題解

第一步,根據圖恢原來的排列.

在得到原來的排列以後,我們從排列中挑選一些位置(p1,p2,...pm)(p_1,p_2,...p_m)組成一個獨立支配集.必然有a[p1]&lt;a[p2]&lt;...&lt;a[pm]a[p_1] &lt; a[p_2] &lt;... &lt; a[p_m],只有這樣,集合裏的點之間纔沒有邊相連,並且還要滿足條件即[pi,pi+1][p_i,p_{i+1}]之間的數要麼大於a[pi+1]a[p_{i+1}],要麼小於a[i]a[i].

並且在排列中不可能存在p0&lt;p1a[p0]&lt;a[p1]p_0 &lt; p_1並且a[p_0] &lt; a[p_1 ],否則的話,它也應該存在於集合當中,應爲它與集合中的所有點都無邊相連.同理,不存在pm+1&gt;pmp_{m+1} &gt; p_m,使得a[pm+1]&gt;a[pm]a[p_{m+1}] &gt; a[p_m].

如果p&lt;qa[p]&lt;a[q]a[p,q]p&lt;q且a[p] &lt; a[q]且a[p,q]之間的數不會存在介於a[p]a[p]a[q]a[q]之間的數,就從ppqq連邊.

答案就是從入度爲00的點,跑到出度爲00的點的路徑數之和.

拓撲序dp一下結束.

代碼

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 105;
int a[maxn], in[maxn], cnt, x[maxn],n,m;
ll dp[maxn];
bool vis[maxn][maxn];
vector<int> G[maxn];

ll dfs(int cur){
	if(dp[cur] != -1) return dp[cur];
	dp[cur] = 0;
	if(G[cur].size() == 0) dp[cur]++;
	for(int nx : G[cur]){
		dp[cur] += dfs(nx);
	}
	return dp[cur];
}

int main(){
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i = 0;i < m;i++){
		int u,v;
		cin>>u>>v;
		vis[u][v] = vis[v][u] = 1;
		if(u < v) swap(u,v);
		G[u].push_back(v);
		in[v]++;
	}
	for(int i = 1;i <= n;i++){
		for(int j = i+1;j <= n;j++){
			if(vis[i][j]) continue;
			G[i].push_back(j);
			in[j]++;
		}
	}
	queue<int> q;
	for(int i = 1;i <= n;i++){
		if(in[i] == 0) q.push(i);
	}
	while(!q.empty()){
		int cur = q.front();q.pop();
		a[cur] = ++cnt;
		for(int nx : G[cur]){
			in[nx]--;
			if(in[nx] == 0) q.push(nx);
		}
	}
	memset(in,0,sizeof(in));
	for(int i = 1;i <= n;i++) G[i].clear();
	for(int i = 1;i <= n;i++){
		for(int j = i+1;j <= n;j++){
			if(a[i] > a[j]) continue;
			bool flag = true;
			for(int k = i + 1;k < j;k++)
				if(a[k] > a[i] && a[k] < a[j]) flag = false;
			if(flag){
				G[i].push_back(j);
				in[j]++;
			}
		}
	}
	ll ans = 0;
	memset(dp,-1,sizeof(dp));
	for(int i = 1;i <= n;i++) if(in[i] == 0) ans += dfs(i);
	cout<<ans<<endl;
	return 0;
}

J.Rabbit vs Turtle

題解

留坑


K.Points and Rectangles

題解

cdqcdq分治的一道比較裸的題.

像這種能用二維線段樹做的題(空間爆炸)都可以轉換成離線cdq分治去解決.

每個點算作11eventevent.

每個矩形按照左邊和右邊拆成兩個eventevent,每個eventevent包含上下邊界.

我們考慮單獨對點和矩形算貢獻.

  1. 對矩形算貢獻:
    所有在該矩形前加的點都對矩形的左邊eventevent11個負的影響,對矩形的右邊eventevent11個正的貢獻.
    維護一個樹狀數組,ii位置的值代表目前存在的點yy值爲ii的有多少個.
    那麼對於每一個分治過程按照eventeventxx從小到大的過程掃過來,遇見矩形左邊eventevent就將貢獻減去sum[down,up]sum[down,up],遇見矩形右邊eventevent,就將貢獻加上sum[down,up]sum[down,up].
    需要注意的一點是當多個eventeventxx相同的情況下,我們按照矩形左邊eventevent,點eventevent,矩形右邊eventevent 順序掃.

  2. 對點算貢獻.
    矩形的左邊eventevent對點會產生正的貢獻,而矩形的右邊eventevent會對點產生負的貢獻.
    還是按照eventeventxx值從小到大的過程掃過來,遇見矩形左邊eventevent就將新樹狀數組的downdown位置+1+1,將up+1up+1位置1-1.
    遇見矩形右邊eventevent就將新樹狀數組的downdown位置1-1,將up+1up+1位置+1+1.
    在遇見點eventevent時候,直接統計sum[0,y]sum[0,y]的值就是包含這個點的矩形的貢獻.
    需要注意的一點是當多個eventeventxx相同的情況下,我們按照矩形左邊eventevent,點eventevent,矩形右邊eventevent 順序掃.

代碼

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(int i = a;i <= b;++i)
#define int long long
int op[3] = {2,1,3};
const int N = 1e6;
struct event{
	int tp,id,x,up,down;
	event(int tp=0,int id=0,int x=0,int up=0,int down=0):tp(tp),id(id),x(x),up(up),down(down){}
	friend bool operator<(event &e1,event &e2) {
		return e1.x == e2.x ? (op[e1.tp] < op[e2.tp]) : (e1.x < e2.x);
	}
}es[N<<1];
int tot;

int a[N],ans[N];
int bit1[N],bit2[N];
int ec = 0,q;

event tmp[N<<1];

int lowbit(int x) {
	return x & (-x);
}

void add(int bit[],int pos,int x) {
	for(;pos < N;pos += lowbit(pos))
		bit[pos] += x;
}

int sum(int bit[],int pos) {
	int res = 0;
	for(;pos;pos -= lowbit(pos)) {
		res += bit[pos];
	}
	return res;
}

void clr(int bit[],int pos) {
	if(pos <= 0) return ;
	for(;pos < N;pos += lowbit(pos))
		bit[pos] = 0;
}

void solve(int l,int r) {
	if(l == r) 
		return ;
	int mid = (l + r) >> 1;
	solve(l,mid);solve(mid+1,r);
	std::vector<int> vec1,vec2;
	int s = 0,p = l,q = mid+1;
	while(p <= mid && q <= r) {
		if(es[p] < es[q]) {
			if(es[p].tp == 0) {
				add(bit1,es[p].up,1);
				vec1.push_back(es[p].up);
			}
			else if(es[p].tp == 1) {
				add(bit2,es[p].down,1);
				add(bit2,es[p].up+1,-1);
				vec2.push_back(es[p].down);
				vec2.push_back(es[p].up+1);
			}
			else if(es[p].tp == 2) {
				add(bit2,es[p].down,-1);
				add(bit2,es[p].up+1,1);
				vec2.push_back(es[p].down);
				vec2.push_back(es[p].up+1);
			}
			tmp[s++] = es[p++];
		}
		else {
			
			if(es[q].tp == 0) {
				ans[es[q].id] += sum(bit2,es[q].up);
			}
			else if(es[q].tp == 1) {
				ans[es[q].id] -= sum(bit1,es[q].up) - sum(bit1,es[q].down-1);
			}
			else if(es[q].tp == 2) {
				ans[es[q].id] += sum(bit1,es[q].up) - sum(bit1,es[q].down-1);
			}
			tmp[s++] = es[q++];
		}
	}
	while(p <= mid) tmp[s++] = es[p++];
	while(q <= r){
		if(es[q].tp == 0) {
			ans[es[q].id] += sum(bit2,es[q].up);
		}
		else if(es[q].tp == 1) {
			ans[es[q].id] -= sum(bit1,es[q].up) - sum(bit1,es[q].down-1);
		}
		else if(es[q].tp == 2)
			ans[es[q].id] += sum(bit1,es[q].up) - sum(bit1,es[q].down-1);
		tmp[s++] = es[q++];
	}
	for(int x : vec1) clr(bit1,x);
	for(int x : vec2) clr(bit2,x);
	rep(i,l,r) {
		es[i] = tmp[i-l];
	}
}

signed main() {
	std::ios::sync_with_stdio(false);
	std::cin >> q;
	a[ec++] = 0;
	rep(i,1,q) {
		int tp;
		std::cin >> tp;
		if(tp == 1) {
			int x,y;
			std::cin >> x >> y;
			x+=2,y+=2;
			a[ec++] = x;a[ec++] = y;
			es[tot++] = event(0,i,x,y,0);
		}	
		else if(tp == 2) {
			int _a,_b,_c,_d;
			std::cin >> _a >> _b >> _c >> _d;
			_a+=2,_b+=2,_c+=2,_d+=2;
			a[ec++] = _a;a[ec++] = _b;a[ec++] = _c;a[ec++] = _d;
			es[tot++] = event(1,i,_a,_d,_b);
			es[tot++] = event(2,i,_c,_d,_b);
		}
	}	
	std::sort(a,a+ec);
	ec = std::unique(a,a+ec)-a;
	for(int i = 0;i < tot;++i) {
		es[i].x = std::lower_bound(a,a+ec,es[i].x)-a;
		es[i].up = std::lower_bound(a,a+ec,es[i].up)-a;
		es[i].down = std::lower_bound(a,a+ec,es[i].down)-a;
	}
	solve(0,tot-1);
	rep(i,1,q) {
		ans[i] += ans[i-1];
		std::cout << ans[i] << std::endl;
	}
	
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章