To_Heart—题解——[UR #19]前进四

感觉这种转换很神仙。没见过。或者是对势能线段树了解不够多?反正很神仙就是了

题目大意

link.

题面有人性化题意/se.

题解

转换一下题意其实就是让你求从后往前每个位置的极长下降子序列长度,带修,板子是 O ( n log ⁡ n 2 ) O(n\log n^2) O(nlogn2) 的!但是数据范围在 1 0 6 10^6 106 最多单 log。

考虑转换,发现一个了不起的性质,就是答案不会超过 n n n

也许你认为这是废话,但并不是。答案是极长下降子序列的长度,每次改变都是减小,而且不会超过 n n n 次改变。为了利用上这个优秀的性质,选择将询问离线后从后往前枚举下标,维护询问。那么修改变成了对一段询问区间取 min,询问变成了对于某个询问单点求 min 值的变化次数 发现这个变换次数刚好可以用势能表示,所以问题用势能线段树即可解决。

这告诉我们什么?答案的改变单调且范围与 n 同阶或者更小的时候,可以考虑利用势能来解释并相对应的提出解决方法。

#include
using namespace std;
int read(){
	int x=0;char c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar();
	return x;
}

struct zz{
	int l,r,tag;
	int mx,cx;
};
int n,m;
vector<int> q[1000005];
struct ss{
	int x,y;
};
vector<ss> v[1000005];

struct Tree{
	#define lc p<<1
	#define rc p<<1|1
	zz t[4000005];
	void Push_Down(int p){
		if(!t[p].tag) return ;
		if(t[p].mx<t[lc].mx) t[lc].mx=t[p].mx,t[lc].tag+=t[p].tag;
		if(t[p].mx<t[rc].mx) t[rc].mx=t[p].mx,t[rc].tag+=t[p].tag;
		t[p].tag=0;
	}
	void Push_Up(int p){
		t[p].mx=max(t[lc].mx,t[rc].mx);
		t[p].cx=max(t[lc].cx,t[rc].cx);
		if(t[p].mx!=t[lc].mx) t[p].cx=max(t[p].cx,t[lc].mx);
		if(t[p].mx!=t[rc].mx) t[p].cx=max(t[p].cx,t[rc].mx);
	}
	void Build_Tree(int p,int l,int r){
		t[p].l=l,t[p].r=r,t[p].tag=0;t[p].mx=0x3f3f3f3f,t[p].cx=-0x3f3f3f3f;
		if(l==r) return void();
		int mid=(t[p].l+t[p].r)>>1;
		Build_Tree(lc,l,mid),Build_Tree(rc,mid+1,r); 
		Push_Up(p);
	}
	void Change_Tree(int p,int l,int r,int x){
		if(t[p].mx<=x) return ;
		if(l<=t[p].l&&t[p].r<=r&&t[p].cx<x) return t[p].mx=x,t[p].tag++,void();
		Push_Down(p);
		int mid=(t[p].l+t[p].r)>>1;
		if(l<=mid) Change_Tree(lc,l,r,x);
		if(mid+1<=r) Change_Tree(rc,l,r,x);
		Push_Up(p);
	}
	int Find_Tree(int p,int l){
		if(t[p].l==t[p].r) return t[p].tag;
		Push_Down(p);
		int mid=(t[p].l+t[p].r)>>1;
		if(l<=mid) return Find_Tree(lc,l);
		else return Find_Tree(rc,l);
	}
}T;

int ans[1000005];
int main(){
	cin>>n>>m;
	for(int i=1,x;i<=n;i++){
		x=read();
		v[i].push_back((ss){x,0});
	}
	for(int i=1,x,y,op;i<=m;i++){
		op=read();
		if(op&1){
			x=read(),y=read();
			v[x].push_back((ss){y,i});
		}
		else{
			x=read();
			q[x].push_back(i);
		}	
	}
	T.Build_Tree(1,1,m);
	for(int i=1;i<=n;i++) v[i].push_back((ss){0,m});
	for(int i=n;i>=1;i--){
		for(int j=0;j+1<v[i].size();j++)
			T.Change_Tree(1,v[i][j].y+1,v[i][j+1].y,v[i][j].x);
		for(auto x:q[i]) ans[x]=T.Find_Tree(1,x);
	}
	for(int i=1;i<=m;i++) if(ans[i]) printf("%d\n",ans[i]);
	return 0;
}


你可能感兴趣的:(数据结构,算法,数据结构)