To_Heart—题解——[HNOI/AHOI2018]转盘

起因是学长来讲数据结构!讲得很好!于是记录一下以后有机会讲给下一代!!1

题目大意

Link.

题意:给定一个 n n n 元环,可以从任意位置出发,每秒可以选择前进或者不动,每个点有一个到达时间 t i t_i ti当到达某个点的时间 T T T 满足 T > t i T>t_i T>ti 时可以标记这个点。问标记所有点的最短的时间。有 m m m 次修改,强制在线。

题解

可以发现如果我们需要停下来等待覆盖一个点,不如将起点向前移。所以可以发现最优的走法一定是没有停留的。

破环成链,发现无论如何一定有 n-1 步,对于每个点作为起点“向前移”的步数是后面每一个到达时间比max,大概就可以写出:

T min ⁡ = min ⁡ i ≤ n { max ⁡ i < j < i + n t j − j + i } + n − 1 T_{\min} = \min _{i\le n} \{ \max _{i<j<i+n} t_j-j+i\} +n - 1 Tmin=inmin{i<j<i+nmaxtjj+i}+n1

然后惊奇地发现对于任意的 i i i ,加入 i + n i+n i+n 之后的点参与表达式计算并不会对答案造成影响,所以可以愉快的写出:

T min ⁡ = min ⁡ i ≤ n { max ⁡ i < j ≤ 2 × n t j − j + i } + n − 1 T_{\min} = \min _{i\le n} \{ \max _{i<j \le 2\times n} t_j-j+i\} +n - 1 Tmin=inmin{i<j2×nmaxtjj+i}+n1

a i = t i + i a_i=t_i+i ai=ti+i,则有:

T min ⁡ = min ⁡ i ≤ n { max ⁡ i < j ≤ 2 × n a j + i } + n − 1 T_{\min} = \min _{i\le n} \{ \max _{i<j \le 2\times n} a_j+i\} +n - 1 Tmin=inmin{i<j2×nmaxaj+i}+n1

暴力显然是 O ( n 2 log ⁡ n ) O(n^2\log n) O(n2logn) 的,但是发现所有的 i 都可以枚举完所有点,这引导我们反过来思考每个点对前面点的贡献。这时候考虑 max 的性质,发现所有可能对答案构成贡献的点的权值是单调递减的,这个序列反过来思考发现是一个从 n 到 1 的极长上升子序列。因为答案要求最小,从后往前看,每个点产生贡献应该尽量提前,所以写出:

T min ⁡ = m i n { a p j + ( p j − 1 + 1 ) } + n − 1 T_{\min} =min\{ a_{p_j}+(p_{j-1}+1)\} +n -1 Tmin=min{apj+(pj1+1)}+n1

其中 p 是找出来的极长上升子序列的下标集合。

然后维护一下极长上升子序列,复杂度为 O ( n log ⁡ n 2 ) O(n \log n^2 ) O(nlogn2)

#include<bits/stdc++.h>
using namespace std;

int n,m,p;

struct zz{
	int l,r;
	int mx;
	int len;
};

int a[2000005];
int t[2000005];

struct Tree{
	#define lc p<<1
	#define rc p<<1|1 
	zz t[20000005];
	void Build_Tree(int p,int l,int r){
		t[p].l=l,t[p].r=r;
		if(t[p].l==t[p].r) return t[p].mx=a[l],t[p].len=::t[l],void();
		int mid=(l+r)>>1;
		Build_Tree(lc,l,mid),Build_Tree(rc,mid+1,r);
		t[p].mx=max(t[lc].mx,t[rc].mx);
		t[p].len=Push_Up(lc,t[rc].mx);
	} 
	int Push_Up(int p,int val){
		if(t[p].l==t[p].r) return t[p].l+max(t[p].mx,val);
		int mid=(t[p].l+t[p].r)>>1;
		if(t[rc].mx>=val) return min(t[p].len,Push_Up(rc,val));
		else return min(Push_Up(lc,val),mid+1+val);
	}
	void Change_Tree(int p,int l){
		if(t[p].l==t[p].r) return t[p].mx=a[l],t[p].len=::t[l],void();
		int mid=(t[p].l+t[p].r)>>1;
		if(l<=mid) Change_Tree(lc,l);
		else Change_Tree(rc,l);
		t[p].mx=max(t[lc].mx,t[rc].mx);
		t[p].len=Push_Up(lc,t[rc].mx);
	}
}T;

int main(){
	cin>>n>>m>>p;
	for(int i=1;i<=n;i++) scanf("%d",&t[i]),a[i]=t[i]-i,t[i+n]=t[i],a[i+n]=t[i+n]-(i+n);
	T.Build_Tree(1,1,n<<1);
	int ans=0;
	printf("%d\n",ans=T.t[1].len+n-1);
	for(int i=1,x,y;i<=m;i++){
		scanf("%d%d",&x,&y);
		if(p) x^=ans,y^=ans;
		t[x]=t[x+n]=y,a[x]=y-x,a[x+n]=y-x-n;
		T.Change_Tree(1,x),T.Change_Tree(1,x+n);
		printf("%d\n",ans=T.t[1].len+n-1);
	} 
	
	return 0;
}
posted @ 2023-07-06 22:30  To_Heart  阅读(6)  评论(0编辑  收藏  举报  来源