【C++】JOISC 2020 Day1原题+翻译+解析+代码

T1 Building4

原题

CSDN下载:https://download.csdn.net/download/Ljnoit/12257726

链接

LOJ-3271
UOJ-501
vjudge

翻译

题目描述

给定长度为 2 N 2N 2N 的两个序列,分别为序列 A : A 1 , A 2 , ⋯ , A A:A_1,A_2,⋯,A A:A1,A2,,A2N ,和序列 B : B 1 , B 2 , ⋯ , B B:B_1,B_2,⋯,B B:B1,B2,,B2N
构造一个长度为 2 N 2N 2N 的序列 C C C 。满足以下条件:

  • 序列 C C C 的第 i i i 个数 C i C_i Ci ,只能从 A i A_i Ai B i B_i Bi 中选取。
  • a a a 为序列 A A A 中元素被选取的次数, b b b 为序列 B B B 中元素被选取的次数,则 a = b = N a=b=N a=b=N
  • 该序列是一个单调上升的序列,不要求严格单调上升

如有多解,任意输出一组解即可。

输入格式

第一行包含一个数字 N N N,表示序列长度的一半。
第二行包含 2 N 2N 2N 个数字,第 i i i 个数字表示序列 A A A 中的第 i i i 个数字 A i A_i Ai
第三行包含 2 N 2N 2N 个数字,第 i i i 个数字表示序列 B B B 中的第 i i i 个数字 B i B_i Bi

输出格式

你不需要直接输出这个序列。
你只需要输出一行长度为 2 N 2N 2N 的字符串 s s s , 如果序列 C C C 的第 i i i 个数从 A i A_i Ai 中选取,则 s i = A s_i=A si=A ,否则 s i = B s_i=B si=B
如果无解,输出一行一个数 − 1 −1 1

样例输入 1

3
2 5 4 9 15 11
6 7 6 8 12 14

样例输出 1

AABABB

样例解释 1

序列 C 为 (2,5,6,9,12,14) ,分别选取的是 A1,A2,B3,A4,B5,B6 ,可以验证序列 C 满足所有条件。

样例输入 2

2
1 4 10 20
3 5 8 13

样例输出 2

BBAA

样例解释 2

多解输出任意解。

样例输入 3

2
3 4 5 6
10 9 8 7

样例输出 3

-1

样例解释 3

无法构造满足条件的排列,输出 −1 。

样例输入 4

6
25 18 40 37 29 95 41 53 39 69 61 90
14 18 22 28 18 30 32 32 63 58 71 78

样例输出 4

BABBABAABABA

数据范围与提示

对于 100 100 100% 的数据,保证

  • 1 ≤ N ≤ 5 × 1 0 5 1≤N≤5×10^5 1N5×105
  • 1 ≤ A i ≤ 1 0 9 ( 1 ≤ i ≤ 2 N ) 1≤A_i≤10^9(1≤i≤2N) 1Ai109(1i2N)
  • 1 ≤ B i ≤ 1 0 9 ( 1 ≤ i ≤ 2 N ) 1≤B_i≤10^9(1≤i≤2N) 1Bi109(1i2N)

子任务 1 ( 11 分): 1 ≤ N ≤ 2000 1≤N≤2000 1N2000
子任务 2 ( 89 分):没有特殊性质。

解析

先不管n个A的限制,从左往右扫一遍,每次选尽量小的,先保证有解。若无解,直接输出"-1"。
考虑有些位置的选择可以改变,x改变了,x+1可能要改变,可能不要改变。
如果x改变了,x+1必须跟着改变,x->x+1连一条边,不难发现有若干条链。
每一条链可以选一个后缀。我这里type=0是A、type=1是B。

代码

#pragma GCC optimize(3,"Ofast","inline")
#pragma G++ optimize(3,"Ofast","inline")

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

#define R                  register int
#define re(i,a,b)          for(R i=a; i<=b; i++)
#define ms(i,a)            memset(a,i,sizeof(a))
#define MAX(a,b)           (((a)>(b)) ? (a):(b))
#define MIN(a,b)           (((a)<(b)) ? (a):(b))

using namespace std;

typedef long long LL;

namespace IO {
	#include <cctype>

	template <typename T>
	inline void read(T &x){
		x=0; 
		char c=0; 
		T w=0;  
		while (!isdigit(c)) w|=c=='-',c=getchar();  
		while (isdigit(c)) x=x*10+(c^48),c=getchar();  
		if(w) x=-x;  
	}
	
	template <typename T>
	inline void write(T x) {
	    if(x<0) putchar('-'),x=-x;
	    if(x<10) putchar(x+'0');
	        else write(x/10),putchar(x%10+'0');
	}
	
	template <typename T>
	inline void writeln(T x) {
	    write(x);
	    putchar('\n');
	}
} 

const int N=1e6+5;

int n;
int a[N],b[N];
int l[2][N],r[2][N];

inline void dfs(R type,R last,R cnt) {
    if(last==0) return;
    if(cnt<0) exit(0);
    if(type==0) {
        if(l[0][last-1]<=cnt-1 && cnt-1<=r[0][last-1] && a[last-1]<=a[last]) dfs(0, last-1, cnt-1);
            else dfs(1,last-1,cnt-1);
        putchar('A');
    } else {
        if(l[0][last-1]<=cnt && cnt<=r[0][last-1] && a[last-1]<=b[last]) dfs(0,last-1,cnt);
            else dfs(1,last-1,cnt);
        putchar('B');
    }
    return;
}

int main() {
    IO::read(n);
    for (R i=1; i<=(n<<1); ++i) IO::read(a[i]);
    for (R i=1; i<=(n<<1); ++i) IO::read(b[i]);
    memset(l,0X3F,sizeof(l));
    memset(r,-1,sizeof(r));
    l[0][1]=r[0][1]=1;
    l[1][1]=r[1][1]=0;
    for(R i=2; i<=(n<<1); ++i) {
        if(a[i]>=a[i-1]) l[0][i]=MIN(l[0][i],l[0][i-1]+1),r[0][i]=MAX(r[0][i],r[0][i-1]+1);
        if(a[i]>=b[i-1]) l[0][i]=MIN(l[0][i],l[1][i-1]+1),r[0][i]=MAX(r[0][i],r[1][i-1]+1);
        if(b[i]>=a[i-1]) l[1][i]=MIN(l[1][i],l[0][i-1]),r[1][i]=MAX(r[1][i],r[0][i-1]);
        if(b[i]>=b[i-1]) l[1][i]=MIN(l[1][i],l[1][i-1]),r[1][i]=MAX(r[1][i],r[1][i-1]);
        if(l[0][i]>r[0][i] && l[1][i]>r[1][i]) {
            puts("-1");
            return 0;
        }
    }
    if(l[0][n<<1]<=n && n<=r[0][n<<1]) {
        dfs(0,n<<1,n);
        putchar('\n');
        return 0;
    }
    if(l[1][n<<1]<=n && n<=r[1][n<<1]) {
        dfs(1,n<<1,n);
        putchar('\n');
        return 0;
    }
    puts("-1");
    return 0;
}

T2 hamburg

原题

CSDN下载:https://download.csdn.net/download/Ljnoit/12257726

链接

LOJ-3272
UOJ-502
vjudge

翻译

题目描述

你听说过奇异物品公司(Just Odd inventions, Ltd.)吗?这家公司以生产奇异物品出名。在本题中,我们简称它为 JOI 公司。

JOI 公司将要举办一场盛大的年会,主厨正在一块由 1 0 9 × 1 0 9 10^9×10^9 109×109 个网格组成的超大的网格形烤架上烤着 N N N 块汉堡肉。为了方便,我们用 ( x , y ) (x, y) (x,y) 表示从左往右数第 x x x 列,从下往上数第 y y y 行的网格。

我们将汉堡肉编号为 1 … N 1…N 1N,其中第 i i i 块汉堡覆盖了左下角 ( L i , D i ) (L_i, D_i) (Li,Di) 到右上角 ( R i , U i ) (R_i, U_i) (Ri,Ui) 的矩形区域,注意这些区域可能重叠。

你是 JOI 公司的萌新,现在你有 K K K 根竹签,你只要把竹签插到一个格子里就可以知道那格的肉有没有熟,你的任务是检查所有的肉是否已经煮熟。你可以把竹签插到一个没有肉的格子里,也可以把几根竹签插到同一格里。

形式化地说,你的任务是寻找一个由 K K K 个二元组 ( x 1 , y 1 ) , … , ( x n , y n ) (x_1,y_1),…,(x_n,y_n) (x1,y1),,(xn,yn) 组成的 K K K 元组(其中元素可以重复),使得:

对于任意 i ∈ [ 1 , N ] i∈[1,N] i[1,N],存在 j j j 使得 L i ≤ x j ≤ R i L_i≤x_j≤R_i LixjRi D i ≤ y j ≤ U i D_i≤y_j≤U_i DiyjUi 成立。
对于任意 j ∈ [ 1 , K ] j∈[1,K] j[1,K],有 1 ≤ x j , y j ≤ 1 0 9 1≤x_j, y_j≤10^9 1xj,yj109
编写一个程序,给出汉堡肉覆盖的区域和竹签的数量,找出一个插竹签的方法。数据保证有解。

输入格式

第一行两个空格分隔的整数 N , K N, K N,K,含义见题面描述。

接下来 N 行每行四个空格分隔的整数 L i , D i , R i , U i L_i, D_i, R_i, U_i Li,Di,Ri,Ui,描述一块汉堡肉。

输出格式

输出 K K K 行,每行输出以空格分隔的两个整数 x j , y j x_j, y_j xj,yj。如果有多解,输出任意一个。

样例输入 1

4 2
2 1 3 3 
1 2 4 3 
6 1 7 4
5 3 7 5

样例输出 1

2 2
7 4

样例解释 1

( 2 , 2 ) (2, 2) (2,2) 处插一根竹签,可以确定汉堡肉 1 1 1 2 2 2 的煮熟情况,在 ( 7 , 4 ) (7, 4) (7,4) 处插一根竹签,可以确定汉堡肉 ( 3 , 4 ) (3,4) (3,4) 的煮熟情况。

另一种可行方案是,在 ( 3 , 3 ) (3, 3) (3,3) ( 6 , 4 ) (6, 4) (6,4) 处分别插一根竹签。

样例输入 2

3 3
1 1 1 1
1 2 1 2
1 3 1 3

样例输出 2

1 1
1 2
1 3

数据范围与提示

对于 100 100 100% 的数据,有 1 ≤ N ≤ 2 × 1 0 5 1≤N≤2×10^5 1N2×105, 1 ≤ K ≤ 4 1≤K≤4 1K4, 1 ≤ L i ≤ R i ≤ 1 0 9 ( 1 ≤ i ≤ N ) 1≤L_i≤R_i≤10^9 (1≤i≤N) 1LiRi109(1iN), 1 ≤ D i ≤ U i ≤ 1 0 9 ( 1 ≤ i ≤ N ) 1≤D_i≤U_i≤10^9 (1≤i≤N) 1DiUi109(1iN),且数据保证有解。

各子任务限制如下:

子任务编号分值附加限制
11N≤2000, K=1
21N≤2000, K=2
33N≤2000, K=3
46N≤2000, K=4
51K=1
63K=2
76K=3
879K=4

解析

本题可以随机。
每次先随机k个矩形,然后把其它矩形加入,每次选面积减小比最小的一个。
最后一个点超时,但还是100.

一般方法:
考虑算出 m a x l , m i n r , m a x d , m i n u maxl,minr,maxd,minu maxl,minr,maxd,minu
先考虑横坐标。
假设 m a x l > m i n r maxl>minr maxl>minr,否则横坐标放在 [ m a x l , m i n r ] [maxl,minr] [maxl,minr]的任意一个位置都行。在 x = m i n r x=minr x=minr这条直线的左边,至少要放一个点,因为 m i n r minr minr是一个矩形的右边界,这个矩形在 x = m i n r x=minr x=minr的左边。又因为这是最左的右边界,所以不如把一个点就放在 x = m i n r x=minr x=minr 上,这样还能覆盖右边的一些矩形。
同理,对 m a x l , m a x d , m i n u maxl,maxd,minu maxl,maxd,minu可以得到类似的结论。
于是得到结论:
在k个点的坐标中, m a x l , m i n r , m a x d , m i n u maxl,minr,maxd,minu maxl,minr,maxd,minu都至少出现了一次。

当k<=3时,有鸽巢原理,至少有一个点的两维坐标都是 m a x l , m i n r , m a x d , m i n u maxl,minr,maxd,minu maxl,minr,maxd,minu中的一个。
枚举这个点,把它覆盖的矩形去掉,递归到k−1的情况。

k=4时,先讨论以上的情况,最后的一种情况就是 m a x l , m i n r , m a x d , m i n u maxl,minr,maxd,minu maxl,minr,maxd,minu围成的矩形A上,每一条边有一个点。
现在考虑其它要被覆盖的矩形B,如果一个矩形B经过了A的三条边,那一定有一条边是完全覆盖,所以矩形B一定会被覆盖不考虑。
剩下的情况就是:

  • 1.B覆盖A的一个角。
  • 2.B覆盖A的两条对边。
  • 3.B覆盖A的一条边。
  • 4.B和A无交

如果出现4说明每条边放一个(不放交点)无解。

对于一条边上的一个区间: [ x , y ] [x,y] [x,y],建立两个点代表它选或不选,
当同一边上两个区间不交时,则只能选一个。

  • 1,2相当于两个区间至少选一个
  • 3相当于这个区间必须选。

用线段树可以优化到 O ( n l o g n ) O(nlogn) O(nlogn)

代码

随机法:

#pragma GCC optimize(3,"Ofast","inline")
#pragma G++ optimize(3,"Ofast","inline")

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <algorithm>

#define R                  register int
#define re(i,a,b)          for(R i=a; i<=b; i++)
#define ms(i,a)            memset(a,i,sizeof(a))
#define MAX(a,b)           (((a)>(b)) ? (a):(b))
#define MIN(a,b)           (((a)<(b)) ? (a):(b))

using namespace std;

typedef long long LL;

namespace IO {
	#include <cctype>

    char buf[1<<20],*p1,*p2;

    #define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin), p1 == p2) ? 0 : *p1++)

	template <typename T>
	inline void read(T &x){
		x=0; 
		char c=0; 
		T w=0;  
		while (!isdigit(c)) w|=c=='-',c=gc();  
		while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=gc();  
		if(w) x=-x;  
	}
	
	template <typename T>
	inline void write(T x) {
	    if(x<0) putchar('-'),x=-x;
	    if(x<10) putchar(x+'0');
	        else write(x/10),putchar(x%10+'0');
	}
	
	template <typename T>
	inline void writeln(T x) {
	    write(x);
	    putchar('\n');
	}
} 

const int N=2e5+5;
const int inf=1e9;

struct node {
	LL l,r,d,u;
} a[N],ans[10];

node merge(node x,node y) {
    return (node){MAX(x.l,y.l),MIN(x.r,y.r),MAX(x.d,y.d),MIN(x.u,y.u)};
}

double check(node x) {
	if(x.l<=x.r && x.d<=x.u) return (x.r-x.l)*(x.u-x.d);
	    else return -1;
}

int n,K;

int main() {
    srand(time(0));
	IO::read(n);
    IO::read(K);
	for(R i=1; i<=n; ++i) {
		IO::read(a[i].l);
        IO::read(a[i].d);
        IO::read(a[i].r);
        IO::read(a[i].u);
	}
	LL flag=0;
	while(!flag) {
		for(R i=1; i<=K; ++i) ans[i]=(node){1,inf,1,inf};
		random_shuffle(a+1,a+1+n);
		LL cnt=0;
		for(R i=1; i<=n; ++i) {	
			double mxsz=-1;
			R u=-1;
			for(R j=1; j<=K; ++j)
				if(check(merge(a[i],ans[j]))/check(ans[j])>mxsz) {
					mxsz=check(merge(a[i],ans[j]))/check(ans[j]);
					u=j;
				}
			if(mxsz>=0) {
				cnt++;	
				ans[u]=merge(a[i],ans[u]);
			}
			if(cnt!=i) break;
		}
		if(cnt==n) flag=1;
	}
	for(R i=1; i<=K; ++i) {
        IO::write(ans[i].l);
        putchar(' ');
        IO::writeln(ans[i].d);
    }
	return 0;
}

一般方法:

#pragma GCC optimize(3,"Ofast","inline")
#pragma G++ optimize(3,"Ofast","inline")

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <algorithm>
#include <random>
#include <cassert>

#define RI                 register int
#define re(i,a,b)          for(RI i=a; i<=b; i++)
#define ms(i,a)            memset(a,i,sizeof(a))
#define MAX(a,b)           (((a)>(b)) ? (a):(b))
#define MIN(a,b)           (((a)<(b)) ? (a):(b))
#define pb                 push_back
#define mk                 make_pair
#define fi                 first
#define se                 second

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;

namespace IO {
	#include <cctype>

	template <typename T>
	inline void read(T &x){
		x=0; 
		char c=0; 
		T w=0;  
		while (!isdigit(c)) w|=c=='-',c=getchar();  
		while (isdigit(c)) x=x*10+(c^48),c=getchar();  
		if(w) x=-x;  
	}
	
	template <typename T>
	inline void write(T x) {
	    if(x<0) putchar('-'),x=-x;
	    if(x<10) putchar(x+'0');
	        else write(x/10),putchar(x%10+'0');
	}
	
	template <typename T>
	inline void writeln(T x) {
	    write(x);
	    putchar('\n');
	}
} 

using IO::read;
using IO::write;
using IO::writeln;

const int MAXN=2e5+5;
const int inf=1e9;
int n,K;
PII p[5];

struct steak {
	int l,r,d,u;
	
	bool contain(PII p) {
		return l<=p.fi && p.fi<=r && d<=p.se && p.se<=u;
	}
	
	void input() {
		read(l),read(d),read(r),read(u);
	}
	
} a[5][MAXN];

int del(steak _old[MAXN],steak _new[MAXN],int oldn,PII p) {
	int newn=0;
	for(int i=1; i<=oldn; ++i)
		if(!_old[i].contain(p))
			_new[++newn]=_old[i];
	return newn;
}

void dfs(int k,int n) {
	int L=0,R=inf+1,D=0,U=inf+1;
	for(int i=1;i<=n;++i) {
		L=MAX(L,a[k][i].l);
		R=MIN(R,a[k][i].r);
		D=MAX(D,a[k][i].d);
		U=MIN(U,a[k][i].u);
	}
	if(L<=R && D<=U) {
		for(int i=1; i<k; ++i) puts("114 514");
		write(L); putchar(' '); writeln(D);
		for(int i=k+1; i<=K; ++i) {
			write(p[i].fi); putchar(' '); writeln(p[i].se);
		}
		exit(0);
	}
	if(k==1) return;
	int newn;
	newn=del(a[k],a[k-1],n,p[k]=mk(L,D)); dfs(k-1,newn);
	newn=del(a[k],a[k-1],n,p[k]=mk(L,U)); dfs(k-1,newn);
	newn=del(a[k],a[k-1],n,p[k]=mk(R,D)); dfs(k-1,newn);
	newn=del(a[k],a[k-1],n,p[k]=mk(R,U)); dfs(k-1,newn);
}

int main() {
	read(n);
	read(K);
	for(int i=1; i<=n; ++i) a[K][i].input();
	dfs(K,n);
	assert(K==4);
	srand((ULL)time(0)^(ULL)(new char));
	while(1) {
		#define a a[K]
		int L=0,R=inf+1,D=0,U=inf+1;
		int Lx=0,Rx=0,Dx=0,Ux=0;
		for(int i=1; i<=n; ++i) {
			if(a[i].l>L) L=a[i].l,Lx=i;
			if(a[i].r<R) R=a[i].r,Rx=i;
			if(a[i].d>D) D=a[i].d,Dx=i;
			if(a[i].u<U) U=a[i].u,Ux=i;
		}
		int Ld=a[Lx].d,Lu=a[Lx].u;
		int Rd=a[Rx].d,Ru=a[Rx].u;
		int Dl=a[Dx].l,Dr=a[Dx].r;
		int Ul=a[Ux].l,Ur=a[Ux].r;
		bool ok=1;
		for(int i=1; i<=n; ++i) {
			if(a[i].l<=L && a[i].r>=L && a[i].d<=Ld && a[i].u>=Lu) continue;
			if(a[i].l<=R && a[i].r>=R && a[i].d<=Rd && a[i].u>=Ru) continue;
			if(a[i].d<=D && a[i].u>=D && a[i].l<=Dl && a[i].r>=Dr) continue;
			if(a[i].d<=U && a[i].u>=U && a[i].l<=Ul && a[i].r>=Ur) continue;
			bool flag=0;
			static int order[4];
			for(int j=0; j<4; ++j) order[j]=j;
			random_shuffle(order,order+4);
			for(int j=0; j<4; ++j) {
				if(order[j]==0) {
					if(a[i].l<=L && a[i].r>=L && MAX(a[i].d,Ld)<=MIN(a[i].u,Lu)){
						Ld=MAX(a[i].d,Ld);
						Lu=MIN(a[i].u,Lu);
						flag=1;
						break;
					}
				} else if(order[j]==1) {
					if(a[i].l<=R && a[i].r>=R && MAX(a[i].d,Rd)<=MIN(a[i].u,Ru)) {
						Rd=MAX(a[i].d,Rd);
						Ru=MIN(a[i].u,Ru);
						flag=1;
						break;
					}
				} else if(order[j]==2) {
					if(a[i].d<=D && a[i].u>=D && MAX(a[i].l,Dl)<=MIN(a[i].r,Dr)) {
						Dl=MAX(a[i].l,Dl);
						Dr=MIN(a[i].r,Dr);
						flag=1;
						break;
					}
				} else {
					if(a[i].d<=U && a[i].u>=U && MAX(a[i].l,Ul)<=MIN(a[i].r,Ur)) {
						Ul=MAX(a[i].l,Ul);
						Ur=MIN(a[i].r,Ur);
						flag=1;
						break;
					}
				}
			}
			if(!flag) {
				ok=0;
				break;
			}
		}
		if(ok) {
			write(L); putchar(' '); writeln(Ld);
			write(R); putchar(' '); writeln(Rd);
			write(Dl); putchar(' '); writeln(D);
			write(Ul); putchar(' '); writeln(U);
			exit(0);
		}
		#undef a
	}
	return 0;
}

T3 sweeping

原题

CSDN下载:https://download.csdn.net/download/Ljnoit/12257726

链接

LOJ-3273
UOJ-503
vjudge

翻译

题目描述

Bitaro 的房间是一个边长为 N N N 的等腰直角三角形。房间内一点用坐标 ( x , y ) (x,y) (x,y) 表示,其中 0 ≤ x ≤ N , 0 ≤ y ≤ N , x + y ≤ N 0≤x≤N,0≤y≤N,x+y≤N 0xN,0yN,x+yN。直角顶点为原点,三角形两腰分别为 x x x 轴与 y y y 轴。

在这里插入图片描述
一天,Bitaro 注意到他的房间满是灰尘。初始时,房间内有 M M M 堆灰尘。第 i ( 1 ≤ i ≤ M ) i (1≤i≤M) i(1iM) 堆灰尘位于点 ( X i , Y i ) (X_i,Y_i) (Xi,Yi)。在同一点可能有多堆灰尘。

现在,Bitaro 打算用扫帚打扫房间。我们认为扫帚是在房间里的一条线段,并且称线段的长度为扫帚的宽度。因为 Bitaro 做事很有条理,他只能按如下两种方式使用扫帚:

  • Bitaro 将扫帚放在房间里,使得扫帚的一个端点位于原点,并且扫帚平行于 y y y 轴。然后,他会沿 x x x 轴正方向水平移动扫帚,直到不能移动为止。在移动过程中,他会保证扫帚始终与 y y y 轴平行,并且一个端点始终在 x x x 轴上。如果扫帚宽度为 l l l,则在 ( x , y ) (x,y) (x,y) 位置的灰尘 ( x < N − l , y ≤ l ) (x<N−l,y≤l) x<Nl,yl将会移动到 ( N − l , y ) (N−l,y) (Nl,y)(在 ( N − l , y ) (N−l,y) (Nl,y) 处可能存在其他堆灰尘)。这个过程称为过程 H H H
  • Bitaro 将扫帚放在房间里,使得扫帚的一个端点位于原点,并且扫帚平行于 x x x轴。然后,他会沿 y y y 轴正方向水平移动扫帚,直到不能移动为止。在移动过程中,他会保证扫帚始终与 x x x 轴平行,并且一个端点始终在 y y y 轴上。如果扫帚宽度为 l,则在 ( x , y ) (x,y) (x,y) 位置的灰尘 ( x ≤ l , y < N − l ) (x≤l,y<N-l) (xl,y<Nl)将会移动到 ( x , N − l ) (x,N−l) (x,Nl)(在 ( x , N − l ) (x,N−l) (x,Nl) 处可能存在其他堆灰尘)。这个过程称为过程 V V V

在 Bitaro 的房间里,会按顺序发生 Q Q Q 个事件。第 j ( 1 ≤ j ≤ Q ) j (1≤j≤Q) j(1jQ) 个事件是以下事件中的一个:

  • Bitaro 计算第 P j P_j Pj 堆灰尘的位置坐标;
  • Bitaro 使用宽度为 L j L_j Lj 的扫帚,进行了过程 H;
  • Bitaro 使用宽度为 L j L_j Lj 的扫帚,进行了过程 V;
  • 一堆新灰尘出现在点 ( A j , B j ) (A_j,B_j) (Aj,Bj) 处。如果在这个事件之前一共有 c c c 堆灰尘,那么这堆灰尘就是房间中的第 ( c + 1 ) (c+1) (c+1) 堆灰尘。

写一个程序,给出房间的腰长,每一堆灰尘的位置坐标和每个事件的细节,求出要求的某堆灰尘的位置坐标。

输入格式

从标准输入读入以下数据,所有输入的值均为整数。
第一行三个整数,分别为 N , M , Q N,M,Q N,M,Q
接下来 M M M 行,每行两个整数 X i , Y i X_i,Y_i Xi,Yi,表示第 i i i 堆灰尘的初始坐标。
接下来 Q Q Q 行,每行表示一个事件,有两或三个整数。设 T j T_j Tj 为第一个整数,每行含义如下:

  • 如果 T j = 1 T_j=1 Tj=1,则这行有两个整数 T j , P j T_j,P_j Tj,Pj。表示 Bitaro 要计算第 Pj 堆灰尘的坐标;
  • 如果 T j = 2 T_j=2 Tj=2,则这行有两个整数 T j , L j T_j,L_j Tj,Lj。表示 Bitaro 用宽度为 Lj 的扫帚进行了过程 H H H
  • 如果 T j = 3 T_j=3 Tj=3,则这行有两个整数 T j , L j T_j,L_j Tj,Lj。表示 Bitaro 用宽度为 Lj 的扫帚进行了过程 V V V
  • 如果 T j = 4 T_j=4 Tj=4,则这行有三个整数 T j , A j , B j T_j,A_j,B_j Tj,Aj,Bj。表示一堆新的灰尘出现在 ( A j , B j ) (A_j,B_j) (Aj,Bj) 位置。

输出格式

对于每个 T j = 1 T_j=1 Tj=1 的事件,输出一行两个整数到标准输出。输出在事件 j j j 发生时第 P j P_j Pj 堆灰尘的位置坐标。

样例输入 1

6 2 10
1 1
4 0
4 2 3
3 3
1 1
4 1 2
2 3
2 0
1 4
3 2
1 3
1 2

样例输出 1

1 3
3 2
3 3
6 0

样例说明 1

初始时,第一堆灰尘位于 (1,1),第二堆灰尘位于 (4,0)。图一描述了房间现在的情况。

在这里插入图片描述
对于第一个事件,第三堆灰尘添加到点 (2,3) 的位置。图二描述了房间现在的情况。
在这里插入图片描述
对于第二个事件,Bitaro 用宽度为 3 的扫帚进行了过程 V。之后,第一堆灰尘移动到了 (1,3),图三描述了房间现在的情况。

在这里插入图片描述

对于第三个事件,Bitaro 计算了第一堆灰尘的坐标 (1,3)。

对于第四个事件,第四堆灰尘添加到点 (1,2) 的位置。图四描述了房间现在的情况。

在这里插入图片描述
对于第五个事件,Bitaro 用宽度为 3 的扫帚进行了过程 H。之后,第一堆灰尘移到了 (3,3),第三堆灰尘移到了 (3,3),第四堆灰尘移到了 (3,2)。图五描述了房间现在的情况。

在这里插入图片描述
对于第六个事件,Bitaro 用宽度为 0 的扫帚进行了过程 H。之后,第二堆灰尘移到了 (6,0)。图六描述了房间现在的情况。

在这里插入图片描述

对于第七个事件,Bitaro 计算了第四堆灰尘的坐标 (3,2)。

对于第八个事件,Bitaro 用宽度为 2 的扫帚进行了过程 V。没有任何灰尘堆移动。图七描述了房间现在的情况。

在这里插入图片描述

对于第九个事件,Bitaro 计算了第三堆灰尘的坐标 (3,3)。

对于第十个事件,Bitaro 计算了第二堆灰尘的坐标 (6,0)。

这组样例满足子任务 1 和子任务 5 的限制。

样例输入 2

9 4 8
2 3
3 1
1 6
4 3
2 6
1 3
2 2
1 4
2 3
1 2
2 4
1 1

样例输出 2

3 6
4 3
7 1
6 3

样例说明 2

这组样例满足子任务 1, 2, 4, 5 的限制。

样例输入 3

8 1 8
1 5
4 4 1
2 6
1 2
2 3
4 2 2
2 5
1 1
1 3

样例输出 3

4 1
3 5
3 2

样例说明 3

这组样例满足子任务 1, 2, 5 的限制。

样例输入 4

7 4 9
1 5
2 2
4 2
5 0
2 6
2 3
1 2
3 6
1 4
3 1
1 1
2 2
1 3

样例输出 4

4 2
5 1
1 6
5 2

样例说明 4

这组样例满足子任务 1, 3, 4, 5 的限制。

样例输入 5

20 5 25
10 6
0 4
2 1
1 0
2 3
2 18
3 9
4 1 5
4 0 2
3 10
4 3 3
3 3
2 9
4 9 1
3 12
1 4
3 19
1 3
1 9
2 1
1 7
1 6
4 3 3
1 10
1 1
1 5
2 0
1 2
2 2
1 7

样例输出 5

2 17
2 17
9 8
0 17
1 17
3 3
10 10
2 17
2 17
0 17

样例说明 5

这组样例满足子任务 1 和子任务 5 的限制。

数据范围与提示

对于全部数据, 1 ≤ N ≤ 1 0 9 , 1 ≤ M ≤ 5 × 1 0 5 , 1 ≤ Q ≤ 1 0 6 1≤N≤10^9,1≤M≤5×10^5,1≤Q≤10^6 1N109,1M5×105,1Q106。保证:

0 ≤ X i , Y i ≤ N , X i + Y i ≤ N ( 1 ≤ i ≤ M ) ; 0≤X_i,Y_i≤N, X_i+Y_i≤N (1≤i≤M); 0Xi,YiN,Xi+YiN(1iM)
1 ≤ P j ≤ M ′ ( 1 ≤ j ≤ Q ) 1≤P_j≤M′ (1≤j≤Q) 1PjM(1jQ),其中 M ′ M′ M 表示当事件 j j j 发生时灰尘的堆数;
0 ≤ L j ≤ N − 1 ( 1 ≤ j ≤ Q ) ; 0≤L_j≤N−1 (1≤j≤Q); 0LjN1(1jQ)
0 ≤ A j , B j ≤ N , A j + B j ≤ N ( 1 ≤ j ≤ Q ) ; 0≤A_j,B_j≤N, A_j+B_j≤N (1≤j≤Q); 0Aj,BjN,Aj+BjN(1jQ)
存在至少一个事件 T j = 1 ( 1 ≤ j ≤ Q ) T_j=1 (1≤j≤Q) Tj=1(1jQ)
详细子任务与附加限制如下表:

子任务附加限制分值
1 M ≤ 2 × 1 0 3 , Q ≤ 5 × 1 0 3 M≤2×10^3,Q≤5×10^3 M2×103,Q5×1031
2T_j=1,2,410
3 T j = 1 , 2 , 3 , X j ≤ X j + 1 , Y j ≥ Y j + 1 ( 1 ≤ j ≤ M − 1 ) T_j=1,2,3, X_j≤X_j+1, Y_j≥Y_j+1 (1≤j≤M−1) Tj=1,2,3,XjXj+1,YjYj+1(1jM1)11
4 T j = 1 , 2 , 3 T_j=1,2,3 Tj=1,2,353
5无附加限制25

解析

线段树分治+平衡树。

对于一开始y随x递增而不递增的部分分,每次修改的都是一段区间。用线段树(平衡树)维护,每次二分出修改的区间,然后区间赋值即可。对于没有4操作的部分,被移动过至少一次点,它们之间满足y随x递增而不递增。所以先对每个点求出第一次移动的时间(二维排序问题),或者平衡树在线维护什么时候移动。在点第一次移动时,丢入一棵维护已经移动过点的平衡树,那棵平衡树做前面说的操作。有4操作时,发现主要是因为新加入的点没有经过前面修改的洗礼,所以不满足那个性质了。对于每个询问,相当于求一个点经过一个区间的操作后的结果。利用线段树分治,把这个区间分成线段树上的log个区间。这样的话,对于线段树上的一个区间,上面的询问都要经过这个区间的修改,就可以当没有4操作那么做了。

时间复杂度: O ( n l o g 2 n ) O(n log2 n) O(nlog2n)

代码

#pragma GCC optimize(3,"Ofast","inline")
#pragma G++ optimize(3,"Ofast","inline")

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <algorithm>
#include <random>
#include <tuple>

#define R                  register int
#define re(i,a,b)          for(R i=a; i<=b; i++)
#define ms(i,a)            memset(a,i,sizeof(a))
#define MAX(a,b)           (((a)>(b)) ? (a):(b))
#define MIN(a,b)           (((a)<(b)) ? (a):(b))

using namespace std;

typedef long long LL;

namespace IO {
	#include <cctype>

    char buf[1<<20],*p1,*p2;

    #define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin), p1 == p2) ? 0 : *p1++)

	template <typename T>
	inline void read(T &x){
		x=0; 
		char c=0; 
		T w=0;  
		while (!isdigit(c)) w|=c=='-',c=gc();  
		while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=gc();  
		if(w) x=-x;  
	}
	
	template <typename T>
	inline void write(T x) {
	    if(x<0) putchar('-'),x=-x;
	    if(x<10) putchar(x+'0');
	        else write(x/10),putchar(x%10+'0');
	}
	
	template <typename T>
	inline void writeln(T x) {
	    write(x);
	    putchar('\n');
	}
} 

const int N=15e5+5;
const int inf=1e9;

mt19937 gen(1145141);

int n,m,clk;
pair<int,int> pts[N],ans[N];
int qt[N],qc[N],tag[N],del[N];
pair<pair<int,int>,int> tp[N];

namespace SegTree {
	pair<int,int> tr[N * 4];

	void build(int i,int l,int r) {
		if(l==r) {
			tr[i]=make_pair(tp[l].second,l);
			return;
		}
		int mid=(l+r)>>1;
		build(i<<1,l,mid);
		build(i<<1|1,mid+1,r);
		tr[i]=(pts[tr[i<<1].first].second < pts[tr[i<<1|1].first].second ? tr[i<<1] : tr[i<<1|1]);
	}

	pair<int,int> query(int i,int l,int r,int lf,int rg) {
		if(lf<=l && r<=rg) return tr[i];
		int mid=(l+r)>>1;
		if(rg<=mid) return query(i<<1,l,mid,lf,rg);
		    else if(lf>mid) return query(i<<1|1,mid+1,r,lf,rg);
		pair<int,int> r1=query(i<<1,l,mid,lf,rg),r2=query(i<<1|1,mid+1,r,lf,rg);
		return ((pts[r1.first].second < pts[r2.first].second) ? r1 : r2);
	}

	void update(int i,int l,int r,int pos) {
		if(l==r) {
			tr[i].first = 0;
			return;
		}
		int mid=(l+r)>>1;
		if (pos<=mid) update(i<<1,l,mid,pos);
		    else update(i<<1|1,mid+1,r,pos);
		tr[i]=(pts[tr[i<<1].first].second < pts[tr[i<<1|1].first].second ? tr[i<<1] : tr[i<<1|1]);
	}
}

namespace Treap {
	struct Node {
		int lson,rson,par,rnd,x,y,tagx,tagy;

		inline void upd(int tx,int ty) {
			x=MAX(x,tx);
			tagx=MAX(tagx,tx);
			y=MAX(y,ty);
			tagy=MAX(tagy,ty);
		}

		inline void pushdown();
	} tr[N];

	int tot,root;

	inline void Init() {
		tot=root=0;
		tr[0].lson=tr[0].rson=tr[0].par=tr[0].rnd=tr[0].x=tr[0].y=tr[0].tagx=tr[0].tagy=0;
	}

	inline int NewNode(int x,int y) {
		int p=++tot;
		tr[p].lson=tr[p].rson=tr[p].par=tr[p].tagx=tr[p].tagy=0;
		tr[p].rnd=gen();
		tr[p].x=x,tr[p].y=y;
		return p;
	}

	inline void Node::pushdown() {
		if(tagx || tagy) {
			if(lson) tr[lson].upd(tagx, tagy);
			if(rson) tr[rson].upd(tagx, tagy);
			tagx=tagy=0;
		}
	}

	inline pair<int,int> Split_y(int p,int y) {
		if(!p) return make_pair(0, 0);
		tr[p].pushdown();
		if(tr[p].y<=y) {
			pair<int,int> q=Split_y(tr[p].lson,y);
			tr[p].lson=q.second;
			if(tr[p].lson) tr[tr[p].lson].par=p;
			q.second=p;
			tr[p].par=0;
			return q;
		} else {
			pair<int,int> q=Split_y(tr[p].rson,y);
			tr[p].rson=q.first;
			if(tr[p].rson) tr[tr[p].rson].par=p;
			q.first=p;
			tr[p].par=0;
			return q;
		}
	}

	inline pair<int,int> Split_x(int p,int x) {
		if(!p) return make_pair(0, 0);
		tr[p].pushdown();
		if(tr[p].x>x) {
			pair<int,int> q=Split_x(tr[p].lson,x);
			tr[p].lson=q.second;
			if (tr[p].lson) tr[tr[p].lson].par=p;
			q.second=p;
			tr[p].par=0;
			return q;
		} else {
			pair<int,int> q=Split_x(tr[p].rson,x);
			tr[p].rson=q.first;
			if(tr[p].rson) tr[tr[p].rson].par=p;
			q.first=p;
			tr[p].par=0;
			return q;
		}
	}

	int Merge(int p1,int p2) {
		if(!p1 || !p2) return p1|p2;
		if(tr[p1].rnd<tr[p2].rnd) {
			tr[p1].pushdown();
			tr[p1].rson = Merge(tr[p1].rson,p2);
			if(tr[p1].rson) tr[tr[p1].rson].par=p1;
			return p1;
		} else {
			tr[p2].pushdown();
			tr[p2].lson=Merge(p1,tr[p2].lson);
			if(tr[p2].lson) tr[tr[p2].lson].par=p2;
			return p2;
		}
	}

	int Insert(int x,int y) {
		int p=NewNode(x, y);
		int a,b,c;
		tie(b,c)=Split_x(root,x);
		tie(a,b)=Split_y(b,y-1);
		root=Merge(Merge(a,p),Merge(b,c));
		return p;
	}

	inline pair<int,int> query(int p) {
		int x=tr[p].x,y=tr[p].y;
		while(p) {
			x=MAX(x,tr[p].tagx);
			y=MAX(y,tr[p].tagy);
			p=tr[p].par;
		}
		return make_pair(x,y);
	}
}

int nid[N];

void solve(int l,int r) {
	if(l==r) return;
	int mid=(l+r)>>1;
	solve(l,mid);
	solve(mid+1,r);
	int tot=0;
	clk++;
	for(int i=l; i<=mid; i++) {
		if(qt[i]==4) {
			tp[++tot]=make_pair(pts[qc[i]],qc[i]);
			tag[qc[i]]=clk;
			del[qc[i]]=0;
		}
	}
	if(!tot) return;
	sort(tp+1,tp+1+tot);
	SegTree::build(1,1,tot);
	Treap::Init();
	for(int i=mid+1; i<=r; i++) {
		if(qt[i]==4 || (qt[i]==1 && tag[qc[i]]!=clk)) continue;
		if(qt[i]==1) {
			ans[i]=(del[qc[i]] ? Treap::query(nid[qc[i]]) : pts[qc[i]]);
		} else if(qt[i]==2) {
			int p,q;
			tie(p,q)=Treap::Split_y(Treap::root,qc[i]);
			if(q) Treap::tr[q].upd(n-qc[i],0);
			Treap::root=Treap::Merge(p,q);
			int xr=lower_bound(tp+1,tp+1+tot,make_pair(make_pair(n-qc[i],n+5),0))-tp-1;
			if(!xr) continue;
			pair<int,int> pr;
			while(true) {
				pr=SegTree::query(1,1,tot,1,xr);
				if(pts[pr.first].second>qc[i]) break;
				nid[pr.first]=Treap::Insert(n-qc[i],pts[pr.first].second);
				del[pr.first]=1;
				SegTree::update(1,1,tot,pr.second);
			}
		} else if(qt[i]==3) {
			int p,q;
			tie(p,q)=Treap::Split_x(Treap::root,qc[i]);
			if(p) Treap::tr[p].upd(0,n-qc[i]);
			Treap::root=Treap::Merge(p, q);
			int xr=lower_bound(tp+1,tp+1+tot,make_pair(make_pair(qc[i],n+5),0))-tp-1;
			if(!xr) continue;
			pair<int,int> pr;
			while(1) {
				pr=SegTree::query(1,1,tot,1,xr);
				if(pts[pr.first].second > n-qc[i]) break;
				nid[pr.first]=Treap::Insert(pts[pr.first].first,n-qc[i]);
				del[pr.first]=1;
				SegTree::update(1,1,tot,pr.second);
			}
		}
	}
	for(int i=l; i<=mid; i++) {
		if(qt[i]==4 && del[qc[i]]) {
			pts[qc[i]]=Treap::query(nid[qc[i]]);
		}
	}
}

int main() {
	int q,cnt;
	IO::read(n);
	IO::read(m);
	IO::read(q);
	for (int i = 1; i <= m; i++) {
		IO::read(pts[i].first);
		IO::read(pts[i].second);
		qt[i]=4;
		qc[i]=i;
	}
	cnt=m;
	for(int i=m+1; i<=m+q; i++) {
		IO::read(qt[i]);
		if (qt[i]<=3) IO::read(qc[i]);
		    else {
                qc[i] = ++cnt;
                IO::read(pts[cnt].first);
                IO::read(pts[cnt].second);
		}
	}
	m+=q;
	clk=0;
	pts[0].second=n+5;
	solve(1,m);
	for(int i=1; i<=m; i++) {
		if(qt[i]==1) printf("%d %d\n", ans[i].first,ans[i].second);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值