首页 > 其他分享 >P3219 [HNOI2012] 三角形覆盖问题&P1222 三角形 题解

P3219 [HNOI2012] 三角形覆盖问题&P1222 三角形 题解

时间:2024-02-06 12:23:53浏览次数:25  
标签:itl ny int 题解 线段 HNOI2012 num 三角形 id

严格单 $\log$ 做法,附实现细节和代码。

考虑从左往右扫描线,发现每次操作是线段上端点 $-1$,插入线段,删除退化成点的线段。

如果某时刻一条线段被另一条完全覆盖,那么这条线段显然不会产生贡献,可以删去。最后得到的线段一定是左端点单调递增时,右端点也单调递增,可以用 set 维护。

当两线段相交时,下方线段上端点的改变并不会影响总覆盖长度。于是想到用小根堆维护相交区域,当有相交区域缩小至 $0$ 时删去。每次更新长度时减少的就是 set 的 $size$ 减去堆的 $size$。

删除是平凡的。插入时,若线段被已有线段包含,则不需插入。否则,将它包含的线段及其之间的交从 set 和堆中删去,并将中间未被覆盖的区域贡献到当前长度。最后再将其与上/下线段之间的交更新。

时间复杂度分析:set 和堆插入删除都是 $O(\log{N})$ 的,初始和结束时两者皆空。插入一条线段时最多在 set 和堆中加入三个元素(线段本身及其与上下线段的交)。因此只有 $O(N)$ 个元素被插入删除,总复杂度 $O(N\log{N})$。

实现细节:

  • 因为堆不支持直接删除,可以使用懒标记,对于每一条线段记录与上方线段的交的编号,在取堆顶元素时判一下即可。因为懒标记,所以不能用 q.size() 直接得到堆中元素个数,要另外记录。

  • 对于线段上端点整体 $-1$ 的情况,虽然纵坐标改变,但横坐标+纵坐标的值不变,可以用此值比较右端点大小。堆中也可用此方法判断线段的交是否已退化成点。

  • 虽然结果在 int 范围内,但计算梯形面积时可能会爆,要开 long long。

  • corner case 很多,要小心 RE 和 边界条件。

最后附上丑陋的代码:

#include<bits/stdc++.h>
using namespace std;
const int N=50005;
#define int long long
int n,num,l[N],cnt; bool v[N*3],vl[N];
int id[N*2];
struct tri{
	int x,y,l;
	bool operator<(const tri &a)const{return x<a.x;}
}t[N];
struct len{
	int x,id;
	bool operator<(const len &a)const{return x>a.x||x==a.x&&id<a.id;}
};
struct seg{
	int y,z,id;
	bool operator<(const seg &a)const{return y<a.y||y==a.y&&z<a.z;}
};
set<seg> s;
priority_queue<len> q;
vector<len> op[N*2];
set<seg>::iterator it,itl;

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0); cin>>n;
	for(int i=1;i<=n;i++){
		cin>>t[i].x>>t[i].y>>t[i].l;
		id[i*2-1]=t[i].x,id[i*2]=t[i].x+t[i].l;
	}int x,xl,zl,qc=0;
	sort(t+1,t+n+1); sort(id+1,id+n*2+1); cnt=unique(id+1,id+n*2+1)-(id+1);
	for(int i=1;i<=n;i++){
		x=lower_bound(id+1,id+cnt+1,t[i].x)-id; op[x].push_back({1,i});
		x=lower_bound(id+1,id+cnt+1,t[i].x+t[i].l)-id; op[x].push_back({-1,i});
	}v[0]=1;
	int nx=-2e9,ny=0,px,py=0,ans=0;
	for(int i=1;i<=cnt;i++){
		while(q.size()&&v[q.top().id]) q.pop();
		while(q.size()&&q.top().x<=id[i]){
			px=nx; nx=q.top().x;
			py=ny; ny-=(nx-px)*(s.size()-qc); 
			if(px!=-2e9) ans+=(py+ny)*(nx-px);
			while(q.size()&&(q.top().x==nx||v[q.top().id])){
				if(!v[q.top().id]) qc--; v[q.top().id]=1; q.pop();
			}
		}
		px=nx; nx=id[i];
		py=ny; ny-=(nx-px)*(s.size()-qc);
		if(px!=-2e9) ans+=(py+ny)*(nx-px);
		for(int j=0;j<op[i].size();j++){
			x=op[i][j].id;
			if(op[i][j].x==-1&&!vl[x]){
				it=s.find({t[x].y,t[x].x+t[x].y+t[x].l}); s.erase(it);
			}
			else if(!vl[x]){
				it=s.lower_bound({t[x].y,-2000000000,-2000000000});
				if(it!=s.begin()){
					if(it!=s.end()){itl=it;itl--;
						if((*itl).y<=t[x].y&&(*itl).z>=t[x].x+t[x].y+t[x].l){vl[x]=1;continue;}
						if((*it).y<=t[x].y&&(*it).z>=t[x].x+t[x].y+t[x].l){vl[x]=1;continue;}
						if(!v[l[(*itl).id]])qc--;v[l[(*itl).id]]=1;
						if((*it).z<=t[x].x+t[x].y+t[x].l)ny+=max(min(id[i]+t[(*it).id].y-(*itl).z,t[(*it).id].y-t[x].y),0ll);
						else{
							ny+=t[x].l; ny-=min(max(t[x].x+t[x].y+t[x].l-id[i]-t[(*it).id].y,0ll)+max((*itl).z-t[x].x-t[x].y,0ll),t[x].l);
						}
						while(it!=s.end()&&(*it).z<=t[x].x+t[x].y+t[x].l){
							xl=(*it).id,vl[xl]=1;zl=(*it).z; it++; if(!v[l[xl]])qc--; v[l[xl]]=1;
							if(it!=s.end()&&(*it).z<=t[x].x+t[x].y+t[x].l) ny+=max(t[(*it).id].y+id[i]-zl,0ll);
							else if(it!=s.end()) ny+=max(min(id[i]+t[(*it).id].y-zl,t[x].x+t[x].y+t[x].l-zl),0ll);
							else ny+=t[x].x+t[x].y+t[x].l-zl; it--; it=s.erase(it);
						}
						if((*itl).z>t[x].x+t[x].y){
							num++,l[(*itl).id]=num,q.push({id[i]+(*itl).z-t[x].x-t[x].y,num}),qc++;
						}
						if(it!=s.end()&&t[x].x+t[x].y+t[x].l>id[i]+t[(*it).id].y){
							num++,l[x]=num,q.push({id[i]+t[x].x+t[x].y+t[x].l-id[i]-t[(*it).id].y,num}),qc++;
						}
					}
					else{itl=it; itl--;
						if((*itl).z>=t[x].x+t[x].y+t[x].l){vl[x]=1;continue;}ny+=t[x].l;
						if((*itl).z>t[x].x+t[x].y){
							num++,l[(*itl).id]=num,q.push({id[i]+(*itl).z-t[x].x-t[x].y,num}),qc++; ny-=(*itl).z-t[x].x-t[x].y;
						}
					}
				}
				else{
					if(it!=s.end()&&(*it).y<=t[x].y&&(*it).z>=t[x].x+t[x].y+t[x].l){vl[x]=1;continue;}
					if(it==s.end()) ny+=t[x].l;
					else if((*it).z>t[x].x+t[x].y+t[x].l){
						ny+=t[x].l; ny-=max(t[x].x+t[x].y+t[x].l-id[i]-t[(*it).id].y,0ll);
					}
					else ny+=t[(*it).id].y-t[x].y; 
					while(it!=s.end()&&(*it).z<=t[x].x+t[x].y+t[x].l){
						xl=(*it).id,vl[xl]=1;zl=(*it).z; it++; if(!v[l[xl]])qc--; v[l[xl]]=1;
						if(it!=s.end()&&(*it).z<=t[x].x+t[x].y+t[x].l) ny+=max(t[(*it).id].y+id[i]-zl,0ll);
						else if(it!=s.end()) ny+=max(min(id[i]+t[(*it).id].y-zl,t[x].x+t[x].y+t[x].l-zl),0ll);
						else ny+=t[x].x+t[x].y+t[x].l-zl; it--; it=s.erase(it);
					}
					if(it!=s.end()&&t[x].x+t[x].y+t[x].l>id[i]+t[(*it).id].y){
						num++,l[x]=num,q.push({id[i]+t[x].x+t[x].y+t[x].l-id[i]-t[(*it).id].y,num}),qc++;
					}
				}
				s.insert({t[x].y,t[x].x+t[x].y+t[x].l,x});
			}
		}
	}
	cout<<fixed<<setprecision(1)<<(double)(ans)/(double)2;
	return 0;
}

实测在 P3219 上跑 $60ms$,目前最优解,比第二快五倍。在 P1222 上 rk4。(可能是数据太水了,因为我调试时还一堆锅的代码都过了)

标签:itl,ny,int,题解,线段,HNOI2012,num,三角形,id
From: https://www.cnblogs.com/skh504535/p/18009517

相关文章

  • P10145 [WC/CTS2024] 线段树 题解
    Link纪念一下场切题。题意:给定一棵(分点不一定为中点)的线段树,给定若干个询问区间,问有多少个线段树上结点的集合,知道了这些结点对应的区间和就可以知道任何一个询问区间的和。从询问区间开始考虑。会发现可以把所有\(a_i\)分成若干个集合,只要知道每个集合的和就可以知道所有询......
  • 浅谈甲类生产厂房应急照明和疏散指示系统的设计问题解析
    摘要:文章结合电气设计项目实践经验,分析了甲类生产厂房消防应急照明和疏散指示系统设计中照明灯和标志灯的设置、疏散走道和疏散通道的规划、集中控制型系统类型的选择、电压等级的选择中存在的问题,总结了相关经验,可以为同类工程提供参考。关键词:甲类生产厂房;消防应急照明和疏散指示......
  • 题解 CF1876B
    题意给定一个数组\([a_1,a_2,a_3.\cdots,a_n]\),一开始所有元素都为白色。你可以选定其中的至少一个元素涂成黑色,共有\(2^n-1\)种涂法。此时对于所有黑色元素\(a_i\),下标为\(i\)的倍数的所有白色元素都将变成绿色。此时数组中所有黑色和绿色元素的最大值记为此种涂法的......
  • 洛谷 P10145 [WC/CTS2024] 线段树 题解--zhengjun
    提供一种考场做法,在思路上和官方题解的差异蛮大的,虽然结果差不多。首先需要发现\([l,r)\)区间可以算出来的充要条件是:如果对于每个选中的节点\(u\),连无向边\((L_u,R_u)\),则当且仅当\(l\)和\(r\)连通时区间\([l,r)\)可以算出来。证明的话,用前缀和理解这些东西,分别考虑......
  • 题解 CF1849D
    萌新的第一篇题解题目大意对于一个初始颜色都为蓝色的数组\(a_1,a_2,\dots,a_n\)其中\(a_n\in\{0,1,2\}\),现在可以进行两个操作:花费\(1\)个金币,将\(a_i\)涂成红色;选择一个红色的\(a_i>0\),将\(a_{i-1}\)或\(a_{i+1}\)涂成红色,同时\(a_i\)减\(1\)。输出金......
  • F. 乘积最大(题解)
    题目今年是国际数学联盟确定的“2000——世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰90周年。在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参加。活动中,主持人给所有参加活动的选手出了这样一道题目:设有一个长度为......
  • [ARC135D] Add to Square 题解
    题目链接点击打开链接题目解法很牛的题!!!先考虑一步很牛的转化:把矩阵黑白染色,且\(i+j\)为奇数的位置的值取反,每次操作变为左上右下\(+v\),左下右上\(-v\)这样有啥好处?操作不会使行和列的和改变考虑答案的下界显然是:\(\max\{\)行的绝对值之和,列的绝对值之和\(\}\)这里给出......
  • luogu2647题解
    首先这道题没有规定选几个。一开始我以为是全选,然后想了个贪心才发现看错了。那我们先来假设选\(m\)个。这个题的每个物品都会影响后面物品的收益,不好看。由于每个物品\(i\)对后选的其他物品减少的收益都是\(R_i\),因此我们在保证总价值不变的情况下转化一下,把该物品的价......
  • P10125 「Daily OI Round 3」Simple 题解
    题目传送门简单模拟,主要考察字符串。首先输入一个char类型的数组,然后直接遍历每一位是否为Acoipp或Svpoll即可。//Simple//codeby:cq_irritater//time:2024/02/04#include<bits/stdc++.h>usingnamespacestd;chara[10];intmain(){//freopen("c......
  • 中文数字的应用及其问题解决之道
    中文数字,也称汉字数字,是中文语言中表示数字的一种方式。它们不仅有着悠久的历史和文化背景,还在日常生活中发挥着重要的作用。本文将探讨中文数字的应用领域,并介绍它们如何解决实际问题。中文数字-阿拉伯数字转换|一个覆盖广泛主题工具的高效在线平台(amd794.com)https:/......