多校冲刺NOIP模拟22 - 混凝土粉末——离线、线段树

此题不提供链接

题目描述

namespace_std 是一名 Minecraft 玩家。

这天,他开 op 拿到了无数的混凝土粉末,准备扔进一个世界里。

这个世界可以视为一个长度为 n n n,宽度为 1,高度无限的平面,其中 ( x , 0 ) (x,0) (x,0) 的位置为基岩,其上方均为空气。

namespace_std 会进行两种操作共计 q q q 次:

1 l r h:在区间 [ l , r ] [l,r] [l,r] 内每个横坐标上方无限高处扔下 h h h 块第 x x x 种混凝土粉末,其中 x x x 是这次操作的序号。混凝土被扔下后会一直下落直到落到另一个方块上方。

2 x y:查询 ( x , y ) (x,y) (x,y) 位置是哪种混凝土。如果 ( x , y ) (x,y) (x,y) 上此时是空气输出 0。

数据规模与约定 1 ≤ n , q ≤ 1 0 6 , 1 ≤ h ≤ 1 0 9 , 1 ≤ y ≤ 1 0 18 1\le n,q\le 10^6,1\le h\le 10^9,1\le y\le 10^{18} 1n,q106,1h109,1y1018

题解

由于操作只有区间加,某处被一种土覆盖后就不会再改变,所以转化一下就是要求某处最早在第几次操作之后被覆盖。

最直接的做法是按时间建主席树然后二分 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n),但是这题只能过 80 分。这时就有人钻研出一个 log ⁡ \log log 的整体二分做法,但是没必要。

再次转化,把询问离线下来,按 x x x 从小到大排序,那么每次操作相当于对一段区间的询问的 y y y 值做区间减。如果某个值被减得 ≤ 0 \le0 0 了,那么就可以把这个询问处理掉了。

这样复杂度优化到了 O ( n log ⁡ n ) O(n\log n) O(nlogn),配合zkw线段树就完全不用担心常数问题。

代码

#include<cstdio>//JZM yyds!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define uns unsigned
#define MOD 
#define MAXN 1000005
#define INF 1e17
#define IF (it->first)
#define IS (it->second)
using namespace std;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0)f^=(s=='-'),s=getchar();
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
	return f?x:-x;
}
int pt[30],lp;
inline void print(ll x,char c='\n'){
	if(x<0)putchar('-'),x=-x;
	pt[lp=1]=x%10;
	while(x>9)x/=10,pt[++lp]=x%10;
	while(lp)putchar(pt[lp--]^48);
	putchar(c);
}
inline ll lowbit(ll x){return x&-x;}
int n,Q,m,p;
struct itn{
	int x,y;ll z;itn(){}
	itn(int X,int Y,ll Z){x=X,y=Y,z=Z;}
}a[MAXN],cz[MAXN];
int b[MAXN],ti[MAXN];
//zkw---
ll f[MAXN*3],lz[MAXN*3];
inline void change(int x,ll d){
	for(f[p+x]=d,x=(p+x)>>1;x;x>>=1)
		f[x]=min(f[x<<1],f[x<<1|1])-lz[x];
}
inline void add(int l,int r,ll d){
	for(l=p+l-1,r=p+r+1;l^1^r;){
		if(~l&1)f[l^1]-=d,lz[l^1]+=d;
		if(r&1)f[r^1]-=d,lz[r^1]+=d;
		l>>=1,r>>=1;
		f[l]=min(f[l<<1],f[l<<1|1])-lz[l];
		f[r]=min(f[r<<1],f[r<<1|1])-lz[r];
	}
	for(l>>=1;l;l>>=1)
		f[l]=min(f[l<<1],f[l<<1|1])-lz[l];
}
int del[MAXN],le,ans[MAXN];
queue<itn>q;
inline void sch(){
	le=0;
	while(!q.empty())q.pop();
	q.push(itn(1,0,0));
	while(!q.empty()){
		itn x=q.front();q.pop();
		if(f[x.x]-x.z>0)continue;
		if(x.x>=p){del[++le]=x.x-p;continue;}
		x.z+=lz[x.x];
		if(f[x.x<<1]-x.z<=0)q.push(itn(x.x<<1,0,x.z));
		if(f[x.x<<1|1]-x.z<=0)q.push(itn(x.x<<1|1,0,x.z));
	}
}
//------
signed main()
{
	freopen("concrete.in","r",stdin);
	freopen("concrete.out","w",stdout);
	n=read(),Q=read();
	memset(f,0x7f,sizeof(f));
	for(int i=1;i<=Q;i++){
		int op=read();
		if(op==1){
			int l=read(),r=read();
			ll h=read();
			cz[i]=itn(l,r,h);
		}else{
			int x=read();ll y=read();
			m++,a[m]=itn(x,m,y),ti[m]=i;
		}
	}
	sort(a+1,a+1+m,[](itn a,itn b){
		return a.x<b.x;
	});
	for(int i=1;i<=m;i++)b[i]=a[i].x;
	for(p=1;p<m+2;p<<=1);
	for(int i=1;i<=m;i++)change(i,a[i].z);
	for(int i=1;i<=Q;i++)if(cz[i].x){
		int l=lower_bound(b+1,b+1+m,cz[i].x)-b;
		int r=lower_bound(b+1,b+1+m,cz[i].y+1)-b-1;
		add(l,r,cz[i].z),sch();
		while(le--){
			int x=del[le+1];
			ans[a[x].y]=i,change(x,f[0]);
		}
	}
	for(int i=1;i<=m;i++){
		if(ans[i]>ti[i])ans[i]=0;
		print(ans[i]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值