[CSP-S模拟测试]:表格(动态开点二维线段树+离散化)

题目传送门(内部题112)


输入格式

  一个数$N$,表示矩形的个数。
  接下来$N$行,每行四个整数$X_a,Y_a,X_b,Y_b$。分别表示每个矩形左下角和右上角的坐标。
  保证$(X_a<X_b,Y_a<Y_b)$。


输出格式

  一行,表示能看到的颜色数量。


样例

样例输入:

3
0 -1 1 1
2 1 3 5
-4 0 5 4

样例输出:

4


数据范围与提示

样例解释:

数据范围:

  对于$10\%$的数据,保证$N\leqslant 100,|X_a,X_b,Y_a,Y_b|\leqslant 100$
  对于$50\%$的数据,保证$N\leqslant 100,000,|X_a,X_b,Y_a,Y_b|\leqslant 1,000$
  对于$80\%$的数据,保证$N\leqslant 100,000,|X_a,X_b,Y_a,Y_b|\leqslant 100,000$
  对于$100\%$的数据,保证$N\leqslant 100,000,|X_a,X_b,Y_a,Y_b|\leqslant 10^9$


题解

其实$Ctrl+Z$是个提示。

然而我的考场代码……

愣是没有想到可以反着来。

反着来有一个很好的性质,就是如果这段已经被覆盖过了,那么现在插入的这个肯定是被压在下面的。

那么可以用二维线段树,存储哪个区间已经被覆盖了即可,如果有一段区间没有被覆盖,那么答案就$+1$即可。

注意离散化和动态开点即可。

随机数据下很优秀,但是极限数据会被卡。

时间复杂度:$\Theta(n^2)$(但是可过)。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
struct rec{int xa,xb,ya,yb;}e[100001];
unordered_map<int,int> mpx,mpy;
int n;
int x[300001],y[300001],topx,topy,cntx,cnty;
int rt,tot,lson[60000000],rson[60000000];
bool tr[60000000];
int ans,res;
void pushup(int x){tr[x]=tr[lson[x]]&tr[rson[x]];}
void add(int &x,int xl,int yl,int xr,int yr,int opt,int XL,int YL,int XR,int YR)
{
	if(!x)x=++tot;
	if(tr[x]||xr<XL||XR<xl||yr<YL||YR<yl)return;
	if(XL<=xl&&xr<=XR&&YL<=yl&&yr<=YR)
	{
		tr[x]=1;
		ans+=res;
		res=0;
		return;
	}
	if(opt)
	{
		int mid=(xl+xr)>>1;
		add(lson[x],xl,yl,mid,yr,0,XL,YL,XR,YR);
		add(rson[x],mid+1,yl,xr,yr,0,XL,YL,XR,YR);
	}
	else
	{
		int mid=(yl+yr)>>1;
		add(lson[x],xl,yl,xr,mid,1,XL,YL,XR,YR);
		add(rson[x],xl,mid+1,xr,yr,1,XL,YL,XR,YR);
	}
	pushup(x);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d%d",&e[i].xa,&e[i].ya,&e[i].xb,&e[i].yb);
		x[++topx]=e[i].xa;x[++topx]=e[i].xb;
		y[++topy]=e[i].ya;y[++topy]=e[i].yb;
		e[i].xb--;e[i].yb--;
		x[++topx]=e[i].xb;y[++topy]=e[i].yb;
	}
	sort(x+1,x+topx+1);sort(y+1,y+topy+1);
	for(int i=1;i<=topx;i++)if(x[i]!=x[i-1])mpx[x[i]]=++cntx;
	for(int i=1;i<=topy;i++)if(y[i]!=y[i-1])mpy[y[i]]=++cnty;
	cntx++;cnty++;
	for(int i=1;i<=n;i++)
	{
		e[i].xa=mpx[e[i].xa];
		e[i].xb=mpx[e[i].xb];
		e[i].ya=mpy[e[i].ya];
		e[i].yb=mpy[e[i].yb];
	}
	for(int i=n;i;i--){res=1;add(rt,1,1,cntx,cnty,1,e[i].xa,e[i].ya,e[i].xb,e[i].yb);}
	printf("%d\n",ans+1);
	return 0;
}


rp++

原文地址:https://www.cnblogs.com/wzc521/p/11779629.html

时间: 2024-10-08 14:45:38

[CSP-S模拟测试]:表格(动态开点二维线段树+离散化)的相关文章

[JZOJ3615]【NOI2014模拟】数列(平面几何+二维线段树)

Description 给定一个长度为n的正整数数列a[i]. 定义2个位置的f值为两者位置差与数值差的和,即f(x,y)=|x-y|+|a[x]-a[y]|. 你需要写一个程序支持2种操作(k都是正整数): Modify x k:将第x个数的值修改为k. Query x k:询问有几个i满足f(x,i)<=k.询问不仅要考虑当前数列,还要考虑任意历史版本,即统计任意位置上出现过的任意数值与当前的a[x]的f值<=k的对数.(某位置多次修改为同样的数值,按多次统计) Main 令F(x,y)=

P2617 Dynamic Rankings (动态开点权值线段树 + 树状数组)

题意:带修求区间k小 题解:回忆在使用主席树求区间k小时 利用前缀和的思想 既然是前缀和 那么我们可以使用更擅长维护前缀和的树状数组 但是这里每一颗权值线段树就不是带版本的 而是维护数组里i号点的权值信息 所以实际上并不是主席树 每一棵和前面一棵并没有共用结点 对于一次修改操作 我们先删去这个点的原信息 再更新进去 树状数组上的点一起跳 可能看代码比较好理解一点 这个方法限制性也很强 必须离线 #include <bits/stdc++.h> using namespace std; cons

[CSP-S模拟测试]:回文(hash+二维前缀和)

题目描述 闲着无聊的$YGH$秒掉上面两道题之后,开始思考有趣的回文串问题了. 他面前就有一个漂浮着的字符串.显然$YGH$是会$manacher$的,于是他随手求出了这个字符串的回文子串个数.但是他不满足于这个问题,他打算搞出一个数据结构,能够快速求出这个字符串下标为$[l,r]$的子串的回文子串个数(相同的回文子串需重复计数).但是这实在是太简单啦,他打算考考辣鸡$YYR$,可是辣鸡至极的$YYR$完全没有思路. 于是,$YGH$扬长而去,在衣袖带起的一小片尘土之中,沉思的$YYR$依旧在那

【二维线段树】20150209测试 千石抚子的三维积木

3. 千石抚子的三维积木(nadeko.cpp/in/out)   3.5s  512MB  10*10=100分 p.s.本题含有一些(本人)黑历史,请自动过滤题目背景…= = 自从蛇切绳被搞掉之后,抚子认识到普通的蛇其实是很和谐的东西.于是她开始养蛇,有天在家无聊就开始用蛇堆积木.由于她是驯蛇高手所以它们都很听话堆上去之后就不会动,并且每条蛇可以被视为一块长方体(这个比喻有点..好吧,接下来都把蛇叫做积木了). 堆积木是在一块W*D的平地上进行的,每堆一个积木时会告诉你它的长宽高和放置的左上

一维动态数组和二维动态数组的创建和使用

#include<stdio.h> #include<malloc.h> void main(){ int *a,n=10,i; /* calloc()函数的原型是:(void *)calloc(unsigned n,unsigned size) calloc()函数用于向系统动态申请n个,每个占sizege字节的内存单元,函数返回值为所申请的内存空间首地址 malloc和calloc主要区别在于,当系统的内存只剩下一些非常小的碎片时,用calloc函数设计的动态数组的时间效率优于

&nbsp; 动态开辟有序二维数组

#include<stdio.h>#include<stdlib.h>int main(){ //int arr[3][4] // int row; int col; scanf_s("%d%d", &row, &col); int  **q = (int **)malloc(sizeof(int)* row);//开辟一块空间 存储一个含有row数组指针的数组 q[0] = (int *)malloc(sizeof(int)*col); for

【c语言】动态开辟一个二维数组

// 动态开辟一个二维数组 #include <stdio.h> #include <stdlib.h> int main() { int i = 0; int j = 0; int line = 0; int row = 0; int **p = NULL; printf("输入行数:"); scanf("%d", &line); printf("\n"); printf("输入列数:");

微信小程序动态生成保存二维码

起源:最近小程序需要涉及到一些推广方面的功能,所以要写一个动态生成二维码用户进行下载分享,写完之后受益良多,特此来分享一下: 一.微信小程序动态生成保存二维码 wxml: <canvas style="width: 350rpx;height: 350rpx;background:#f1f1f1;" canvas-id="mycanvas"/> js: // pages/qrcode/qrcode.js var QR = require("..

codevs 3981 动态最大子段和(线段树)

题目传送门:codevs 3981 动态最大子段和 题目描述 Description 题目还是简单一点好... 有n个数,a[1]到a[n]. 接下来q次查询,每次动态指定两个数l,r,求a[l]到a[r]的最大子段和. 子段的意思是连续非空区间. 输入描述 Input Description 第一行一个数n. 第二行n个数a[1]~a[n]. 第三行一个数q. 以下q行每行两个数l和r. 输出描述 Output Description q行,每行一个数,表示a[l]到a[r]的最大子段和. 样