首页 > 其他分享 >[JOI 2016 Final]断层 题解

[JOI 2016 Final]断层 题解

时间:2023-05-03 19:45:34浏览次数:52  
标签:nx int 题解 seg lt 坐标 JOI 2016 op

题目链接

首先发现斜着平移比较难处理,所以考虑将平面逆时针旋转 \(45°\)。

接着发现风化也不好处理,但是风化的一定不会作为答案,所以我们可以离线,然后倒着处理操作,上升变为下降。

我们发现每个初始 \(0\) 点最后的坐标就是它正着做时初始的坐标,且每次操作都只会将连续一段点的 \(x,y\) 坐标修改。

我们可以开两棵线段树,一棵维护 \(x\) 坐标,一棵维护 \(y\) 坐标。

容易发现,操作规律:

对于操作一,将维护 \(y\) 坐标的线段树中所有 \(x\) 坐标小于 \(x_i\) 的 \(y\) 坐标减去 \(2 \cdot d_i\)。

对于操作二,将维护 \(x\) 坐标的线段树中所有 \(y\) 坐标大于 \(x_i\) 的 \(x\) 坐标增加 \(2 \cdot d_i\)。

至于为什么乘二,是因为旋转后中间有空隙。

但是我们发现这个修改位置无法直接确定,于是我们可以在线段树上二分修改的位置。当修改 \(y\) 坐标时,我们在 \(x\) 线段树上二分小于 \(x_i\) 的最大的 \(x\),当修改 \(x\) 坐标时,在 \(y\) 线段树上二分大于 \(x_i\) 的最小的 \(y\)。

至于为什么能二分:

显然相邻的两个 \(0\) 的 相对位置是不变的,所以小于一个值的坐标一定在一个区间内。

二分到位置后修改,注意到有可能搜不到这个点,此时不修改就行了。

code:

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define debug cout<<"debug\n"
#define vi vector<int>
#define imp map<int,int>
#define il inline
#define pb push_back
using namespace std;
const int N=2e5+5,inf=0x7fffffff;
int n,q;
ll seg[3][N],a[N][5],lt[3][N],ans[3][N];
int ls(int x){
	return x<<1;
}
int rs(int x){
	return x<<1|1;
}
void build(int l,int r,int p){
	if(l==r){
		seg[0][p]=seg[1][p]=l;
		return;
	}
	
	int mid=(l+r)>>1;
	build(l,mid,ls(p));
	build(mid+1,r,rs(p));
	
	seg[0][p]=min(seg[0][ls(p)],seg[0][rs(p)]);
	seg[1][p]=min(seg[1][ls(p)],seg[1][rs(p)]);
}
void pushdown(int op,int l,int r,int p){
	seg[op][ls(p)]+=lt[op][p];
	seg[op][rs(p)]+=lt[op][p];
	lt[op][ls(p)]+=lt[op][p];
	lt[op][rs(p)]+=lt[op][p];
	lt[op][p]=0;
}
void getans(int l,int r,int p){
	if(l==r){
		ans[0][l]=seg[0][p];
		ans[1][l]=seg[1][p];
		return;
	}
	
	pushdown(0,l,r,p);
	pushdown(1,l,r,p);
	
	int mid=(l+r)>>1;
	getans(l,mid,ls(p));
	getans(mid+1,r,rs(p));
}
void findx(int nx,int ny,int l,int p,int &ans){
	if(nx==ny){
		if(seg[0][p]<=l){
			ans=nx;
		}
		return;
	}
	
	pushdown(0,nx,ny,p);
	int mid=(nx+ny)>>1;
	
	if(l>=seg[0][rs(p)]){
		findx(mid+1,ny,l,rs(p),ans);
	}else{
		findx(nx,mid,l,ls(p),ans);
	}
}
void findy(int nx,int ny,int l,int p,int &ans){
	if(nx==ny){
		if(seg[1][p]>l){
			ans=nx;
		}
		return;
	}
	
	pushdown(1,nx,ny,p);
	int mid=(nx+ny)>>1;
	
	if(l<seg[1][rs(p)]){
		ans=min(ans,mid+1);
		findy(nx,mid,l,ls(p),ans);
	}else{
		findy(mid+1,ny,l,rs(p),ans);
	}
}
void change(int op,int nx,int ny,int l,int r,int p,ll d){
	if(l<=nx&&ny<=r){
		seg[op][p]+=d;
		lt[op][p]+=d;
		return;
	}
	
	pushdown(op,nx,ny,p);
	
	int mid=(nx+ny)>>1;
	
	if(l<=mid){
		change(op,nx,mid,l,r,ls(p),d);
	}
	if(mid<r){
		change(op,mid+1,ny,l,r,rs(p),d);
	}
	
	seg[op][p]=min(seg[op][ls(p)],seg[op][rs(p)]);
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>q;
	for(int i=1;i<=q;i++){
		cin>>a[i][0]>>a[i][1]>>a[i][2];
	}
	build(1,n,1);
	for(int i=q;i>=1;i--){
		int x=a[i][0],y=a[i][1],z=a[i][2];
		if(y==1){
			int res=-inf;
			findx(1,n,x,1,res);
			if(res!=-inf){
				change(1,1,n,1,res,1,-2*z);
			}
		}else{
			int res=inf;
			findy(1,n,x,1,res);
			if(res!=inf){
				change(0,1,n,res,n,1,2*z);
			}
		}
	}
	getans(1,n,1);
	for(int i=1;i<=n;i++){
		cout<<(ans[0][i]-ans[1][i])/2<<"\n";
	}
	return 0;
}

标签:nx,int,题解,seg,lt,坐标,JOI,2016,op
From: https://www.cnblogs.com/victoryang-not-found/p/17369568.html

相关文章

  • 【23.05.03】好题题解
    好题题解A题目大意:计算一个项数为\(n\)的多项式除以\(x^3-x\)的余数多项式。数据范围:对于\(100\%\)的数据:\(2\leqn\leq2\times10^5\)解题分析:水题,直接多项式除法模拟即可。需要注意细节。ACCode:#include<bits/stdc++.h>usingnamespacestd;#d......
  • 【题解】ABC300 F,G
    F.MoreHolidays题目分析:考虑刻画一下我们选择是什么样子的。考虑我们最后选择的\(T\)中的一段一定是形如:一个完整的S选择一个后缀\(+\)若干个完整的S\(+\)一个完整的S的前缀。这样的话就启示我们直接枚举这个前后缀选择的是什么,然后就可以很快算出来了,但是枚举前......
  • [POI2005]SAM-Toy Cars 题解(贪心+堆)
    题面首先考虑一个贪心策略:当地板已经放满需要取出一个时,取下一次使用时间\(nxt\)最晚的那个。所以我们只需要一个可以快速求出一个集合中\(nxt\)最小的点并删除,插入新点的数据结构,这里很容易想到堆。代码很简洁,注意数组的下标是位置还是颜色(考场100pts到0pts)。code:......
  • Valhalla Siege 题解
    题目传送门一道二分题。先观察数据范围,\(1\len,q\le200,000\),显然需要\(O(n\logn)\)的复杂度。且\(1\lek_i\le10^{14}\),需要开longlong。我们需要二分到第一个血量大于伤害值的武士的位置,前面的武士都死了。而在C++算法库中,有一个函数upper_bound(底层原理是二分)正......
  • [蓝桥杯 2017 国 C] 合根植物 题解
    题目传送门一道并查集模板题。没什么好说的,先给个并查集模板,神犇可以直接跳过。查找根:intfind_root(intn){if(fa[n]==n)returnn;returnfa[n]=find_root(fa[n]);}合并:voidmerge(intx,inty){intsx=find_root(x),sy=find_root(y);......
  • Cashier 题解
    题目传送门一道贪心题。我们可以记录每一位客人离开的时间,当下一位客人来临时,他们之间空闲的时间就是我们休息的时间。for(inti=1;i<=n;i++){intt,l;cin>>t>>l;ans+=(t-endt)/a;endt=t+l;}最后再加上所有客人都走后的空闲时间......
  • [ABC213D] Takahashi Tour 题解
    题目传送门一道dfs序题。题目中高桥每次只会去最小的那个点,所以要先对整张图进行排序。for(inti=1;i<=n;i++)sort(g[i].begin(),g[i].end());然后考虑dfs。高桥不会走重复的点,所以我们可以开一个vis数组进行标记。然后我们需要处理高桥君如果无路可走会返回上......
  • Codeforces Round 869 (Div. 2) A-D题解
    比赛地址A.Politics题意:有n个人对m个决案进行投票,对于每一个决案如果票数相同则所有人都离场,反之票数少的一方离场,现在提前知道了每个人的意见,让一些人参与投票,在保证第一个人不离场的情况下最终剩余人数最多是多少Solution把和第一个意见不同的给去掉就行了voidsolve(){......
  • AT_abc106_d [ABC106D] AtCoder Express 2 题解
    题目传送门解题思路区间\(dp\)。划分阶段:以左右城市之间的列车数量为阶段。状态表达:设\(f_{i,j}\)为城市\(i\)与城市\(j\)之间的列车数量。状态转移:由图可知,城市\(l\)与城市\(r\)之间的列车数量,就是城市\(l\)与城市\(r-1\)之间的列车数量与城市\(l+1\)与......
  • CF1817C Similar Polynomials 题解
    可能更好的阅读体验题目传送门Div.1C拉格朗日差值,赛时开香槟。题目大意给定\(d\)次两个多项式\(A(x),B(x)\)。求\(s\),使得\(B(x)\equivA(x+s)\pmod{10^9+7}\),保证\(s\)存在。给出多项式的形式为给出\(x=0,1,\cdots,d\)模\(10^9+7\)的值。\(d\le2.5\times......