CF526F Pudding Monsters 题解

CF526F Pudding Monsters 题解


知识点

分治。


分析

输入的点是 \((x_i,y_i)\),我们把它记为 \(a_{x_i} \gets y_i\),以此转换到序列上。

我们发现,一段区间 \([l,r]\) 最多只有一种合法情况,且需满足 \(\max_{i=l}^{r}a_i - \min_{i=l}^{r}a_i + 1 = r-l+1\),这条式子也可以解释为:区间 \([l,r]\) 上的数值按顺序排放后恰好是连续的。

那么我们就可以分治了。

假设我们此时所在区间为 \([l,r]\),中点即为 \(mid\),那么左端点位于 \([l,mid]\),右端点位于 \((mid,r]\)

枚举左端点 \(i\),对于区间 \(\max,\min\),有三种情况:

  1. 右端点 \(j\)\((mid,it_0]\),区间 \(\max,\min\) 都在 \([i,mid]\) 中;
  2. 右端点 \(j\)\((it_0,it_1]\),区间 \(\max,\min\) 其中之一在 \([i,mid]\) 中;
  3. 右端点 \(j\)\((it_1,r]\),区间 \(\max,\min\) 都不在 \([i,mid]\) 中;

那么就非常简单,对于 1,直接分支判断,对于 2、3,将 \(\max_{i=l}^{r}a_i - \min_{i=l}^{r}a_i + 1 = r-l+1\) 这条式子移项转换一下,将定项与不定项分开,再都开个桶数组即可。


CODE

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)>(b)?(b):(a))
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define DOR(i,a,b) for(register int i=(a);i>=(b);--i)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0);return Main();}signed Main
using namespace std;
const int N=3e5+10,B=N-10;
int n;
ll ans;
int it[2],a[N],cnt[N<<1][3],mi[N],mx[N];
#define mid (l+r>>1)
void Separation(int l,int r){
	if(l==r)return ++ans,void();
	Separation(l,mid),Separation(mid+1,r);
	mi[mid]=mx[mid]=a[mid],mi[mid+1]=mx[mid+1]=a[mid+1],it[0]=it[1]=mid;
	FOR(i,mid+2,r)mx[i]=max(mx[i-1],a[i]),mi[i]=min(mi[i-1],a[i]);
	DOR(i,mid-1,l)mx[i]=max(mx[i+1],a[i]),mi[i]=min(mi[i+1],a[i]);
	FOR(i,mid+1,r)if(l<=i-mx[i]+mi[i]&&i-mx[i]+mi[i]<=mid)++cnt[i-mx[i]+mi[i]][0];
	DOR(i,mid,l){
		while(it[1]<r&&(mi[i]<=mi[it[1]+1]||mx[i]>=mx[it[1]+1])){
			++it[1],++cnt[it[1]+mi[it[1]]][1],++cnt[B+it[1]-mx[it[1]]][2];
			if(l<=it[1]-mx[it[1]]+mi[it[1]]&&it[1]-mx[it[1]]+mi[it[1]]<=mid)
				--cnt[it[1]-mx[it[1]]+mi[it[1]]][0];
		}
		while(it[0]<r&&(mi[i]<=mi[it[0]+1]&&mx[i]>=mx[it[0]+1]))
			++it[0],--cnt[it[0]+mi[it[0]]][1],--cnt[B+it[0]-mx[it[0]]][2];
		ans+=(mid<i+mx[i]-mi[i]&&i+mx[i]-mi[i]<=it[0])+cnt[i][0];
		ans+=(mx[i]>mx[it[1]]?cnt[i+mx[i]][1]:cnt[B+i-mi[i]][2]);
	}
	FOR(i,mid+1,r)if(l<=i-mx[i]+mi[i]&&i-mx[i]+mi[i]<=mid)cnt[i-mx[i]+mi[i]][0]=0;
	FOR(i,mid+1,r)cnt[i+mi[i]][1]=0,cnt[B+i-mx[i]][2]=0;
}
#undef mid
signed main(){
	cin>>n;
	FOR(i,1,n){
		int x,y;
		cin>>x>>y,a[x]=y;
	}
	Separation(1,n);
	cout<<ans<<endl;
	return 0;
}

反思

来久违的写个反思吧。

首先,考场上我想到了 B 题与 E 题的做法,但是 B 题在犹豫之后,多想了一些条件,导致我直接跳过了它,而 E 题则是条件想得过于多了,导致打了好久,最后也没打出来。

C、D 题直接被我舍弃了,在 A 题上花的时间也略多。


提示

这题也可以用线段树做。

posted @ 2024-05-02 20:53  Add_Catalyst  阅读(29)  评论(0)    收藏  举报