【IOI2018】【luoguP4898】 seats 排座位 (线段树)

题面

🔗
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题解

一个矩形,我们可以翻译成只有四个角的非空网格图形。

一个非空的任意网格图形至少有四个角,所以我们只需要维护每个前缀的角的数量,以及角的数量达到最小值的位置个数。

由于不同位置的角一共有 O ( H W ) O(HW) O(HW) 个,每次询问涉及的角有 O ( 1 ) O(1) O(1) 个,完全可以对每个角分别讨论。

假设一个角长这样(把 A 围起来,与 BCD 相邻):

A|B
-·
C D

那么它存在的条件是满足以下任意一个命题

  • A 在前缀中,BC 不在前缀中(前缀端点在区间 [ A , min ⁡ ( B , C ) − 1 ] [A,\min(B,C)-1] [A,min(B,C)1]
  • A 不在前缀中,BC 都在前缀中(前缀端点在区间 [ max ⁡ ( B , C ) , A − 1 ] [\max(B,C),A-1] [max(B,C),A1]

在对应区间做区间加减就可以了,最终全局查询。

时间复杂度 O ( H W log ⁡ 2 ( H W ) + Q log ⁡ 2 ( H W ) ) O(HW\log_2(HW)+Q\log_2(HW)) O(HWlog2(HW)+Qlog2(HW)) ,常数较大。

CODE

一开始没看到标号在 0 ∼ k − 1 0\sim k-1 0k1 之间,以为标号连续就行了,把我给吓傻了

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 1000005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
//#define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
int x[MAXN],y[MAXN],N;
struct it{
	int mi,nm;
	it(){mi=0x3f3f3f3f;nm=0;}
}tre[MAXN<<2];
it operator + (it a,int b) {a.mi+=b;return a;}
it& operator += (it &a,int b) {a.mi+=b;return a;}
inline it merg(it a,it b) {
	if(a.mi != b.mi) return a.mi < b.mi ? a:b;
	a.nm += b.nm; return a;
}
int M,lz[MAXN<<2];
void maketree(int n) {
	M = 1; while(M<n+2) M<<=1;
	for(int i = 1;i <= n;i ++) tre[M+i].nm = 1,tre[M+i].mi = 0;
	for(int i = M-1;i > 0;i --) tre[i] = merg(tre[i<<1],tre[i<<1|1]);
	return ;
}
void addtree(int l,int r,int y) {
	if(l > r) return ;
	for(int s=M+l-1,t=M+r+1;s || t;s >>= 1,t >>= 1) {
		if(s<M) tre[s] = merg(tre[s<<1],tre[s<<1|1]) + lz[s];
		if(t<M) tre[t] = merg(tre[t<<1],tre[t<<1|1]) + lz[t];
		if((s>>1) ^ (t>>1)) {
			if(!(s&1)) tre[s^1] += y,lz[s^1] += y;
			if(t & 1) tre[t^1] += y,lz[t^1] += y;
		}
	} return ;
}
it findtree(int l,int r) {
	it ls = it(),rs = it(); if(l > r) return ls;
	for(int s=M+l-1,t=M+r+1;s || t;s >>= 1,t >>= 1) {
		ls += lz[s]; rs += lz[t];
		if((s>>1) ^ (t>>1)) {
			if(!(s&1)) ls = merg(ls,tre[s^1]);
			if(t & 1) rs = merg(rs,tre[t^1]);
		}
	}return merg(ls,rs);
}
vector<int> a[MAXN];
const int dx[4] = {1,0,-1,0};
const int dy[4] = {0,1,0,-1};
bool f[MAXN];
void addp(int i) {
	if(!i || f[i]) return ;
	int s = x[i],o = y[i];
	for(int j = 0;j < 4;j ++) {
		int l = a[s+dx[j]][o+dy[j]],r = a[s+dx[(j+1)&3]][o+dy[(j+1)&3]];
		if(!l) l = N+1; if(!r) r = N+1;
		addtree(i,min(l,r)-1,1);
		addtree(max(l,r),i-1,1);
	} f[i] = 1;
	return ;
}
void delp(int i) {
	if(!i || !f[i]) return ;
	int s = x[i],o = y[i];
	for(int j = 0;j < 4;j ++) {
		int l = a[s+dx[j]][o+dy[j]],r = a[s+dx[(j+1)&3]][o+dy[(j+1)&3]];
		if(!l) l = N+1; if(!r) r = N+1;
		addtree(i,min(l,r)-1,-1);
		addtree(max(l,r),i-1,-1);
	} f[i] = 0;
	return ;
}
int main() {
	n = read(); m = read(); int Q = read();
	for(int i = 0;i <= n+1;i ++) {
		a[i].resize(m+2);
	}
	maketree(N=n*m);
	for(int i = 1;i <= N;i ++) {
		x[i] = read()+1; y[i] = read()+1;
		a[x[i]][y[i]] = i;
	}
	for(int i = 1;i <= N;i ++) addp(i);
	for(int i = 1;i <= Q;i ++) {
		s = read()+1;o = read()+1;
		delp(s); delp(o);
		for(int j = 0;j < 4;j ++) {
			delp(a[x[s]+dx[j]][y[s]+dy[j]]);
			delp(a[x[o]+dx[j]][y[o]+dy[j]]);
		}
		swap(a[x[s]][y[s]],a[x[o]][y[o]]);
		swap(x[s],x[o]); swap(y[s],y[o]);
		addp(s); addp(o);
		for(int j = 0;j < 4;j ++) {
			addp(a[x[s]+dx[j]][y[s]+dy[j]]);
			addp(a[x[o]+dx[j]][y[o]+dy[j]]);
		}
		putnum(tre[1].nm); cout<<endl;
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值