2023ACM暑假训练day 8 线段树

DAY 8 线段树

训练地址:传送门

训练情况简介

2023-07-04
早上:AB
下午:C
晚上:DEF
AB 单点修改,区间查询最值和最大值
C 区间修改+离散化+整体查询
D 区间替换修改+整体查询
E 区间修改+区间查询(板)
F 区间修改->单点修改+优化,区间查询
早上学了普通的线段树,下午学了线段树的区间修改操作,晚上补了个人训练的E题,加深了线段树的印象
2023-07-04 22:45:54 星期二下机,明日继续

2023-07-05
早上:G
下午:HI
晚上:J
G 题推荐,练练线段树+二分优化DP
H 题线段树,单点修改+区间查询连续子区间最大和,很有意思
I 题与 H 题类似,也是求区间最大连续字段和,题好,但是有点题意还没读懂,日后不推荐二次做,推荐理解
J题两个线段树,一个区间和,一个区间最值(用来剪枝)

2023-07-06 09:25:42 星期四
早上:K
下午:L
K 题与J题类似,双线段树,一个区间和,一个区间最值(用来剪枝)
L 题需要一点思维,想出来之后就是简单的单点修改+区间查询最小值
M 题与HI题类似,是一个给定区间括号匹配数问题

A 题

hdu 1166 敌兵布阵
题意:
单点修改,区间求和
思路:
今天使用线段树解决问题,之前的day3训练使用了树状数组解决问题

//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
单点修改,区间查询
*/
const int maxm=5e4+5,inf=0x3f3f3f3f,mod=998244353;
int n,seg[4*maxm],a[maxm];
string ss;

void push_up(int p){
	seg[p]=seg[p*2]+seg[p*2+1];
	return ;
}

void build(int p,int pl,int pr){
	if(pl==pr){
		seg[p]=a[pl];
		a[pl]=p;
		return ;
	}
	int mid=(pl+pr)>>1;
	build(p*2,pl,mid);
	build(p*2+1,mid+1,pr);
	push_up(p);
	return ;
}

void update(int p,int x){//单点修改
	p=a[p];
	while(p){
		seg[p]+=x;
		p/=2;
	}
	return ;
}

int query(int l,int r,int p,int pl,int pr){//区间查询
	if(l<=pl&&r>=pr) return seg[p];
	int mid=(pl+pr)>>1,res=0;
	if(l<=mid) res+=query(l,r,p*2,pl,mid);
	if(r>mid) res+=query(l,r,p*2+1,mid+1,pr);
	return res;
}

void solve(){
	int c;
	cin>>c;
	for(int z=1;z<=c;++z){
		cout<<"Case "<<z<<":\n";
		cin>>n;
		for(int i=1;i<=n;++i){
			cin>>a[i];
		}
		build(1,1,n);
		int x,y,ans;
		while(cin>>ss){
			if(ss=="End") break;
			cin>>x>>y;
			if(ss=="Add"){
				update(x,y);
			}else if(ss=="Sub"){
				update(x,-y);
			}else{//"Query"
				ans=query(x,y,1,1,n);
				cout<<ans<<'\n';
			}
		}
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

B 题

hdu 1754 I Hate It
题意:
单点修改,区间查询最大值
思路:
线段树板子即可
记得关流或者快读,不然会TLE

//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
单点修改,区间查询最大值
*/
const int maxm=2e5+5,inf=0x3f3f3f3f,mod=998244353;
int n,m,a[maxm],seg[maxm*4];

void push_up(int p){
	seg[p]=max(seg[p<<1],seg[(p<<1)+1]);
	return ;
}

void build(int p,int pl,int pr){
	if(pl==pr){
		seg[p]=a[pl];
		a[pl]=p;
		return ;
	}
	int mid=(pl+pr)>>1;
	build(p<<1,pl,mid);
	build((p<<1)+1,mid+1,pr);
	push_up(p);
	return ;
}

void update(int x,int y){
	x=a[x];
	seg[x]=y;
	x>>=1;
	while(x){
		push_up(x);
		x>>=1;
	}
	return ;
}

int query(int l,int r,int p,int pl,int pr){
	if(l<=pl&&pr<=r) return seg[p];
	int ans=0,mid=(pl+pr)>>1;
	if(l<=mid) ans=max(ans,query(l,r,p<<1,pl,mid));
	if(r>mid) ans=max(ans,query(l,r,(p<<1)+1,mid+1,pr));
	return ans;
}

void solve(){
	while(cin>>n){
		cin>>m;
		for(int i=1;i<=n;++i){
			cin>>a[i];
		}
		build(1,1,n);
		string ss;
		int x,y,ans;
		for(int i=0;i<m;++i){
			cin>>ss>>x>>y;
			if(ss=="Q"){
				ans=query(x,y,1,1,n);
				cout<<ans<<'\n';
			}else{//"U"
				update(x,y);
			}
		}
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

C 题

百炼oj 2528:Mayor's posters
题意:
给你n个区间,按照顺序给出在一面墙上贴海报(海报位置贴在区间[l,r]),问最后有多少张海报能显示出来
思路:
推荐题,区间修改
利用线段树区间修改完成区间的覆盖,最后统计数量即可
另外就是利用离散化的思想,使得区间的范围大大缩小,使得线段树可以实现

//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
区间修改+离散化简化操作
最后整体查询
*/
const int maxm=1e4+5,maxn=2e4+5,inf=0x3f3f3f3f,mod=998244353;
int n,seg[maxn<<4],len,ans;
bool vis[maxn];
struct node{
	int l,r;
}p[maxm];

int pre(){
	int cnt=0;
	for(int i=1;i<=n;++i){
		cin>>p[i].l>>p[i].r;
		seg[++cnt]=p[i].l;
		seg[++cnt]=p[i].r;
		seg[++cnt]=p[i].r+1;//防止出现[1,2][2,3][1,3]离散后三张海报变两张的情况
	}
	sort(seg+1,seg+1+cnt);
	int l=unique(seg+1,seg+1+cnt)-seg-1;//去重
	for(int i=1;i<=n;++i){
		p[i].l=lower_bound(seg+1,seg+1+l,p[i].l)-seg;
		p[i].r=lower_bound(seg+1,seg+1+l,p[i].r)-seg;
	}
	return l;
}

int ls(int p){ return p<<1; }
int rs(int p){ return p<<1|1; }

void push_up(int p){
	seg[p]=seg[ls(p)]+seg[rs(p)];
	return ;
}

void build(int p,int pl,int pr){//建树
	seg[p]=0;
	if(pl==pr){ return ; }
	int mid=(pl+pr)>>1;
	build(ls(p),pl,mid);
	build(rs(p),mid+1,pr);
	return ;
}

void push_down(int p,int pl,int pr){
	if(seg[p]>0){//有海报时覆盖,不然会对原海报产生影响
		seg[ls(p)]=seg[rs(p)]=seg[p];
		seg[p]=0;
	}
	return ;
}

void update(int l,int r,int k,int p,int pl,int pr){
	if(l<=pl&&pr<=r){
		seg[p]=k;
		return ;
	}
	push_down(p,pl,pr);//新的覆盖,让旧的覆盖push_down
	int mid = (pl+pr)>>1;
	if(l<=mid) update(l,r,k,ls(p),pl,mid);
	if(mid<r) update(l,r,k,rs(p),mid+1,pr);
	return ;
}

void query(int p,int pl,int pr){
	if(seg[p]){//遇到已经覆盖了的海报就直接停止继续寻找的行为
		if(!vis[seg[p]]){//遇到一个新的海报++ans
			++ans;
			vis[seg[p]]=true;
		}
		return ;
	}
	if(pl==pr) return ;
	int mid=(pl+pr)>>1;
	if(pl<=mid) query(ls(p),pl,mid);
	if(mid<pr) query(rs(p),mid+1,pr);
	return ;
}

int ask(int l,int r){
	mem(vis,false);
	ans=0;
	vis[0]=true;//标记0不是海报
	query(1,1,len);
	return ans;
}

void solve(){
	cin>>n;
	len=pre();
	build(1,1,len);//建树
	for(int i=1;i<=n;++i){
		update(p[i].l,p[i].r,i,1,1,len);
	}
	cout<<ask(1,len)<<'\n';
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

D 题

hdu 1698 Just a Hook
题意:
给你一个长度为 n 的初始均为 1 的数组,每次选其中连续的一段进行替换成数字 1 或 2 或 3 ,问你最后整个数组的和是多少
思路:
线段树区间修改,整个区间查询
代码详见提交

//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
区间替换修改+整体查询
*/
const int maxm=1e5+5,inf=0x3f3f3f3f,mod=998244353;
int n,a[maxm],seg[maxm<<2],q,tag[maxm<<2];

int ls(int p){ return p<<1; }
int rs(int p){ return p<<1|1; }

void push_up(int p){
	seg[p]=seg[ls(p)]+seg[rs(p)];
	return ;
}

void build(int p,int pl,int pr){
	tag[p]=0;
	if(pl==pr){
		seg[p]=1;
		return ;
	}
	int mid=(pl+pr)>>1;
	build(ls(p),pl,mid);
	build(rs(p),mid+1,pr);
	push_up(p);
	return ;
}

void change(int p,int pl,int pr,int k){
	seg[p]=(pr-pl+1)*k;
	tag[p]=k;
	return ;
}

void push_down(int p,int pl,int pr){
	if(tag[p]){
		int mid=(pl+pr)>>1;
		change(ls(p),pl,mid,tag[p]);
		change(rs(p),mid+1,pr,tag[p]);
		tag[p]=0;
	}
	return ;
}

void update(int l,int r,int k,int p,int pl,int pr){
	if(l<=pl&&pr<=r){
		change(p,pl,pr,k);
		return ;
	}
	push_down(p,pl,pr);
	int mid=(pl+pr)>>1;
	if(l<=mid) update(l,r,k,ls(p),pl,mid);
	if(mid<r) update(l,r,k,rs(p),mid+1,pr);
	push_up(p);
	return ;
}

void solve(){
	int cs;
	cin>>cs;
	for(int i=1;i<=cs;++i){
		cin>>n;
		build(1,1,n);
		cin>>q;
		int x,y,z;
		while(q--){
			cin>>x>>y>>z;
			update(x,y,z,1,1,n);
		}
		cout<<"Case "<<i<<": The total value of the hook is "<<seg[1]<<".\n";
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
	// cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

E 题

百炼oj 3243:A Simple Problem with Integers
题意:
让你实现区间修改数组,区间查询数组
思路:
线段树区间修改区间查询,不多说了

//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*

*/
const int maxm=1e5+5,inf=0x3f3f3f3f,mod=998244353;
ll n,q,seg[maxm<<2],tag[maxm<<2],a[maxm];

int ls(int p){return p<<1;}
int rs(int p){return p<<1|1;}
 
void push_up(int p){//更新seg
	seg[p]=seg[ls(p)]+seg[rs(p)];
	return ;
}
 
void build(int p,int pl,int pr){//建树
	tag[p]=0;
	if(pl==pr){
		seg[p]=a[pl];
		a[pl]=p;
		return ;
	}
	int mid=(pl+pr)>>1;
	build(ls(p),pl,mid);
	build(rs(p),mid+1,pr);
	push_up(p);
	return ;
}

void addtag(int p,int pl,int pr,ll k){//修改tag
	tag[p]+=k;
	seg[p]+=(pr-pl+1)*k;//已经对seg的值进行了修改
	return ;
}
 
void push_down(int p,int pl,int pr){//下传tag
	if(tag[p]!=0){
		int mid=(pl+pr)>>1;
		addtag(ls(p),pl,mid,tag[p]);
		addtag(rs(p),mid+1,pr,tag[p]);
		tag[p]=0;
	}
	return ;
}
 
void update(int l,int r,ll k,int p,int pl,int pr){//区间修改
	if(l<=pl&&pr<=r){
		addtag(p,pl,pr,k);
		return ;
	}
	push_down(p,pl,pr);
	int mid=(pl+pr)>>1;
	if(l<=mid) update(l,r,k,ls(p),pl,mid);
	if(mid<r) update(l,r,k,rs(p),mid+1,pr);
	push_up(p);
	return ;
}

ll query(int l,int r,int p,int pl,int pr){
	if(l<=pl&&pr<=r) return seg[p];
	push_down(p,pl,pr);
	ll res=0;
	int mid=(pl+pr)>>1;
	if(l<=mid) res+=query(l,r,ls(p),pl,mid);
	if(mid<r) res+=query(l,r,rs(p),mid+1,pr);
	return res;
}

void solve(){
	cin>>n>>q;
	for(int i=1;i<=n;++i) cin>>a[i];
	build(1,1,n);
	string ss;
	ll x,y,c;
	ll ans;
	while(q--){
		cin>>ss>>x>>y;
		if(ss=="Q"){
			ans=query(x,y,1,1,n);
			cout<<ans<<'\n';
		}else{
			cin>>c;
			update(x,y,c,1,1,n);
		}
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

F 题

hdu 4027 Can you answer these queries?
题意:
给你 n 个邪恶的战舰排成一排,每一个战舰有一个对应的邪恶值。我们的指挥官决定使用我们的秘密武器来消灭战列舰。对于我们的秘密武器的每一次攻击,它都可以降低连续部分战列舰的续航力,使它们的耐力达到其原始续航力值的平方根。而指挥官想要知道,在多次使用时候,一些连续部分的战舰的邪恶值之和
思路:
You can assume that the sum of all endurance value is less than \(2^{63}\).
先考虑一件事情,就是,开多少次平方,\(2^{63}\)次方也会被开下来?7次
所以这题,单调修改就行,因为改着改着该变成1的早就变成1了。
所以,本题为单点修改+优化,区间查询
优化:update里面添加一句if(seg[p]<=pr-pl+1) return ;
因为这个式子说明,区间[pl,pr]内都是1,或者0(有的话),其内部所有都不用再计算了
当然,区间修改,统计tag,修改后标记一下,应该也行
另外就是输入战舰的序号的时候,x和y可能大小关系未知?(没看出来ing~)故需要排序一下
还是值得做的

//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*

*/
const int maxm=1e5+5,inf=0x3f3f3f3f,mod=998244353;
ll n,a[maxm],m,seg[maxm<<2];

int ls(int p){return p<<1;}
int rs(int p){return p<<1|1;}
 
void push_up(int p){//更新seg
	seg[p]=seg[ls(p)]+seg[rs(p)];
	return ;
}
 
void build(int p,int pl,int pr){//建树
	if(pl==pr){
		seg[p]=a[pl];
		return ;
	}
	int mid=(pl+pr)>>1;
	build(ls(p),pl,mid);
	build(rs(p),mid+1,pr);
	push_up(p);
	return ;
}
 
void update(int l,int r,int p,int pl,int pr){//单点修改
	if(seg[p]<=pr-pl+1) return ;
	if(pl==pr){
		seg[p]=sqrt(seg[p]);
		return ;
	}
	int mid=(pl+pr)>>1;
	if(l<=mid) update(l,r,ls(p),pl,mid);
	if(mid<r) update(l,r,rs(p),mid+1,pr);
	push_up(p);
	return ;
}

ll query(int l,int r,int p,int pl,int pr){
	if(l<=pl&&pr<=r){
		return seg[p];
	}
	int mid=(pl+pr)>>1;
	ll res=0;
	if(l<=mid) res+=query(l,r,ls(p),pl,mid);
	if(mid<r) res+=query(l,r,rs(p),mid+1,pr);
	return res;
}

void solve(){
	int cs=1;
	while(cin>>n){
		cout<<"Case #"<<cs<<":\n";
		for(int i=1;i<=n;++i) cin>>a[i];
		build(1,1,n);
		cin>>m;
		ll c,x,y;
		while(m--){
			cin>>c>>x>>y;
			if(x>y) swap(x,y);
			if(c==0){//update
				update(x,y,1,1,n);
			}else{//query
				ll ans=query(x,y,1,1,n);
				cout<<ans<<'\n';
			}
		}
		cout<<'\n';
		++cs;
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

G 题 未出,题解补

cf 474 E. Pillars
题意:
给你n个高度 h (顺序固定)和一个数 d ,要求你在其中找一个子序列,满足对于该子序列,相邻元素高度差的绝对值大于等于d,你需要找到最长的这样的子序列
思路:
一下子就联想到最长递增子序列的相关做法,但其在此处不适用
DP就是统计每个高度前后的最优解加上当前位置取最大值即可,思路简单,但是会TLE
\(O(n^2)\) DP 超时,故想到利用线段树快速找到一个位置前的DP最大值,再更新当前位置继续遍历
因为题目给的数字较为巨大,故还得配上离散化的思想
利用线段树优化DP,值得一做
有空我想用树状数组来一发,不知是否可行 [ ]待完成记

//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
前面的最优解不一定是后面的最优解
故应该取前后的最优解形成最优解
利用线段树优化DP
晕~
*/
const int maxm=1e5+5,inf=0x3f3f3f3f,mod=998244353;
ll n,d,a[maxm],b[maxm],seg[maxm<<2],dp[maxm];

int ls(int p){return p<<1;}
int rs(int p){return p<<1|1;}

void push_up(int p){
	seg[p]=max(seg[ls(p)],seg[rs(p)]); return ;
}

void build(int p,int pl,int pr){
	if(pl==pr){ seg[p]=0; return ; }
	int mid=(pl+pr)>>1;
	build(ls(p),pl,mid);
	build(rs(p),mid+1,pr);
	push_up(p);
	return ;
}

void update(int l,int r,ll k,int p,int pl,int pr){
	if(pl==pr){ seg[p]=max(seg[p],k); return ; }
	int mid=(pl+pr)>>1;
	if(l<=mid) update(l,r,k,ls(p),pl,mid);
	if(mid<r) update(l,r,k,rs(p),mid+1,pr);
	push_up(p);
	return ;
}

ll query(int l,int r,int p,int pl,int pr){
	if(l<=pl&&pr<=r) return seg[p];
	ll ans=0;
	int mid=(pl+pr)>>1;
	if(l<=mid) ans=max(ans,query(l,r,ls(p),pl,mid));
	if(mid<r) ans=max(ans,query(l,r,rs(p),mid+1,pr));
	return ans;
}

void print(int x,int v,ll p){
	for(int i=x;i>=1;--i){
		if(dp[i]==v && (a[i]>=p+d || a[i]<=p-d)){
			print(i,v-1,a[i]);
			cout<<i<<' ';
			break;
		}
	}
	return ;
}

void solve(){
	cin>>n>>d;
	for(int i=1;i<=n;++i){
		cin>>a[i];
		b[i]=a[i];
	}
	sort(b+1,b+1+n);//用于离散化到1~n
	build(1,1,n);
	ll ans=0;
	int pp;
	for(int i=1;i<=n;++i){
		//二分查找两边区间[~,<=a[i]-d]与[>=a[i]+d,~]的最大值
		int id1=lower_bound(b+1,b+1+n,a[i]+d)-b;
		int id2=upper_bound(b+1,b+1+n,a[i]-d)-b-1;//-1为了前挪一个位置
		int pos=lower_bound(b+1,b+1+n,a[i])-b;
		if(id2>=1 && b[id2]<=a[i]-d)
			dp[i]=max(dp[i],query(1,id2,1,1,n));
		if(id1<=n && b[id1]>=a[i]+d)
			dp[i]=max(dp[i],query(id1,n,1,1,n));
		++dp[i];//加上本身
		update(pos,pos,dp[i],1,1,n);//单点修改,利用线段树找dpmax
		if(dp[i]>ans){//dp[i]表示到i位置的最优解
			ans=dp[i];
			pp=i;
		}
	}
	cout<<ans<<'\n';
	print(pp-1,ans-1,a[pp]);//递归输出答案
	cout<<pp<<'\n';
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

相关资料;
https://blog.csdn.net/qq_37451344/article/details/82904204

H 题 未出,题解补

洛谷 P4513 小白逛公园
题意:
给你n个依次排列的公园,每个公园有一个分数,后面会执行两个操作:一是修改某个公园的分数,二是给你一个游玩公园的位置区间,问你去游玩这个区间里面连续的公园的得分和的最大值
思路:
线段树单点修改+不一样的区间查询
单点修改不必多说,这里就说说怎么找连续子区间的最大和
连续子区间?是否可以联想到分治法求解最大连续子序列?其实这也同理
我们对每一个p管辖的区间都设置几个新的参数:ans,sum,maxl,maxr
ans: 当前 p 区间里的最大连续和
sum: p 区间的和
maxl: 包含 p 区间左端点的最大连续和
maxr: 包含 p 区间右端点的最大连续和

之后对于 p 区间的左右两个子区间,我们可以得到以下的push_up()函数:

void push_up(int p){
	seg[p].sum=seg[ls(p)].sum+seg[rs(p)].sum;
	seg[p].maxl=max(seg[ls(p)].maxl,seg[ls(p)].sum+seg[rs(p)].maxl);
	seg[p].maxr=max(seg[rs(p)].maxr,seg[rs(p)].sum+seg[ls(p)].maxr);
	seg[p].ans=max(seg[ls(p)].maxr+seg[rs(p)].maxl,max(seg[ls(p)].ans,seg[rs(p)].ans));
	return ;
}

同理于区间查询,我们也可以对于以类似于上面的push_up()操作实现新区间的查询

node query(int p,int l,int r){
	/*
	简单的返回sum或ans都是无法满足最优这个条件的
	那么只能返回node了
	再统计区间的最优解!
	*/
	int pl=seg[p].l,pr=seg[p].r;
	if(l<=pl&&pr<=r){
		return seg[p];
	}
	int mid=(pl+pr)>>1;
	if(r<=mid) return query(ls(p),l,r);//查询区间全左
	else if(l>mid) return query(rs(p),l,r);//查询区间全右
	else{//查询区间左右均有,需合并
		node t,x,y;
		x=query(ls(p),l,r);
		y=query(rs(p),l,r);
		t.sum=x.sum+y.sum;
		t.maxl=max(x.maxl,x.sum+y.maxl);
		t.maxr=max(y.maxr,y.sum+x.maxr);
		t.ans=max(max(x.ans,y.ans),x.maxr+y.maxl);
		return t;
	}
}

至此,此题目的最大问题,如何寻找已经完成,至于修改问题即为简单,单点修改和push_up更新即可
具体代码如下:

//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
单点修改,区间查询连续子区间最大值
*/
const int maxm=5e5+5,inf=0x3f3f3f3f,mod=998244353;
int n,m,a[maxm];

struct node{
	int l,r,maxl,maxr,sum,ans;
}seg[maxm<<4];

int ls(int p){ return p<<1; }
int rs(int p){ return p<<1|1; }

void push_up(int p){
	seg[p].sum=seg[ls(p)].sum+seg[rs(p)].sum;
	seg[p].maxl=max(seg[ls(p)].maxl,seg[ls(p)].sum+seg[rs(p)].maxl);
	seg[p].maxr=max(seg[rs(p)].maxr,seg[rs(p)].sum+seg[ls(p)].maxr);
	seg[p].ans=max(seg[ls(p)].maxr+seg[rs(p)].maxl,max(seg[ls(p)].ans,seg[rs(p)].ans));
	return ;
}

void build(int p,int pl,int pr){
	seg[p].l=pl;
	seg[p].r=pr;
	if(pl==pr){
		seg[p].ans=seg[p].maxl=seg[p].maxr=seg[p].sum=a[pl];
		return ;
	}
	int mid=(pl+pr)>>1;
	build(ls(p),pl,mid);
	build(rs(p),mid+1,pr);
	push_up(p);
	return ;
}

void update(int p,int x,int k){
	int pl=seg[p].l,pr=seg[p].r;
	if(pl==pr){
		seg[p].ans=seg[p].maxl=seg[p].maxr=seg[p].sum=k;
		return ;
	}
	int mid=(pl+pr)>>1;
	if(x<=mid) update(ls(p),x,k);
	else update(rs(p),x,k);
	push_up(p);
	return ;
}

node query(int p,int l,int r){
	/*
	简单的返回sum或ans都是无法满足最优这个条件的
	那么只能返回node了
	再统计区间的最优解!
	*/
	int pl=seg[p].l,pr=seg[p].r;
	if(l<=pl&&pr<=r){
		return seg[p];
	}
	int mid=(pl+pr)>>1;
	if(r<=mid) return query(ls(p),l,r);
	else if(l>mid) return query(rs(p),l,r);
	else{
		node t,x,y;
		x=query(ls(p),l,r);
		y=query(rs(p),l,r);
		t.sum=x.sum+y.sum;
		t.maxl=max(x.maxl,x.sum+y.maxl);
		t.maxr=max(y.maxr,y.sum+x.maxr);
		t.ans=max(max(x.ans,y.ans),x.maxr+y.maxl);
		return t;
	}
}

void solve(){
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		cin>>a[i];
	}
	build(1,1,n);
	int c,x,y;
	node t;
	while(m--){
		cin>>c>>x>>y;
		if(c==1){//query
			if(x>y) swap(x,y);
			t=query(1,x,y);
			cout<<t.ans<<'\n';
		}else{//update
			update(1,x,y);
		}
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

相关资料:
https://www.luogu.com.cn/blog/lc-2018-Canton/solution-p4513

I 题 未出 题解补

洛谷 P7492 [传智杯 #3 决赛] 序列
题意:
你有一个长为 n 的序列 a ,现在要对其进行 m 次操作。每次操作可以选择询问指定区间[l,r]的最大连续子段和,或者对区间[l,r]内的\(a_i\)均位或上一个k
思路:
整体代码与H题相同,故可以参考H题的代码,这里给出本题不同的地方
本题利用一个tag标记表示p所在区间的元素全部位与后的结果,利用这个可以标识,对于一个k,如果说 \(tag[p] | k=tag[p]\),那么这个k位或了之后不会产生任何影响,所以这个k就不需要进行update了。
另外就是文中标注的max问题,我没有很清楚的发现,连续字段和可以不取?很疑惑。。。所以得和 0 取大,或者说那里不取大,最终结果取大也行。
下为代码:

//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*

*/
const int maxm=1e5+5,inf=0x3f3f3f3f,mod=998244353;
ll n,m,a[maxm],tag[maxm<<2];

int ls(int p) { return p<<1; }
int rs(int p) { return p<<1|1; }

struct node{
	ll ans,sum,maxl,maxr;
}seg[maxm<<2];

void push_up(int p){
	seg[p].sum=seg[ls(p)].sum+seg[rs(p)].sum;
	seg[p].maxl=max(seg[ls(p)].maxl,seg[ls(p)].sum+seg[rs(p)].maxl);
	seg[p].maxr=max(seg[rs(p)].maxr,seg[rs(p)].sum+seg[ls(p)].maxr);
	seg[p].ans=max(max(seg[ls(p)].ans,seg[rs(p)].ans),seg[ls(p)].maxr+seg[rs(p)].maxl);
	tag[p]=tag[ls(p)]&tag[rs(p)];
	return ;
}

void build(int p,int pl,int pr){
	if(pl==pr){
		tag[p]=a[pl];
		seg[p].sum=a[pl];
		seg[p].ans=max(0ll,a[pl]);//???取max???
		seg[p].maxl=max(0ll,a[pl]);
		seg[p].maxr=max(0ll,a[pl]);
		return ;
	}
	int mid=(pl+pr)>>1;
	build(ls(p),pl,mid);
	build(rs(p),mid+1,pr);
	push_up(p);
	return ;
}

void update(int l,int r,ll k,int p,int pl,int pr){
	if((tag[p]|k)==tag[p]) return ;//剪枝
	if(pl==pr){
		tag[p]|=k;
		seg[p].sum=tag[p];
		seg[p].ans=max(0ll,tag[p]);
		seg[p].maxl=max(0ll,tag[p]);
		seg[p].maxr=max(0ll,tag[p]);
		return ;
	}
	int mid=(pl+pr)>>1;
	if(l<=mid) update(l,r,k,ls(p),pl,mid);
	if(mid<r) update(l,r,k,rs(p),mid+1,pr);
	push_up(p);
	return ;
}

node query(int l,int r,int p,int pl,int pr){
	if(l<=pl&&pr<=r) return seg[p];
	int mid=(pl+pr)>>1;
	if(r<=mid) return query(l,r,ls(p),pl,mid);
	if(l>mid) return query(l,r,rs(p),mid+1,pr);
	node t,x=query(l,r,ls(p),pl,mid),y=query(l,r,rs(p),mid+1,pr);
	t.sum=x.sum+y.sum;
	t.maxl=max(x.maxl,x.sum+y.maxl);
	t.maxr=max(y.maxr,y.sum+x.maxr);
	t.ans=max(max(x.ans,y.ans),x.maxr+y.maxl);
	return t;
}

void solve(){
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		cin>>a[i];
	}
	build(1,1,n);
	ll op,x,y,k;
	node t;
	while(m--){
		cin>>op>>x>>y;
		if(op==1){//query
			t=query(x,y,1,1,n);
			cout<<t.ans<<'\n';
		}else{//update
			cin>>k;
			update(x,y,k,1,1,n);
		}
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

思路来自洛谷题解

J 题

cf 920 F. SUM and REPLACE
题意:
给你n个数,按照顺序排列,再进行m次操作。每次操作要么是问你区间[l,r]的和,要么是让你将区间[l,r]的所有数\(a_i=D(a_i),D(i)=i的因子数\),如:\(D(2)=2 (因子:1,2),D(6)=4 (因子:1,2,3,6)\)
思路:
与上面的F题有点像,其实可以很容易知道,在不超过10次左右的次数,数字就会变成1或2,所以单点修改,打上标记判断即可。但本题的特殊性在于,所有数的最后收敛到1或2,这如果简单的用区间和,其实不好判断。为此,我们牺牲空间,再增加一个线段树,一个标识区间和,为了query操作;一个标识区间最大值,为了update操作。当区间最大值为2时,整个p所代表的区间都已经达到稳定了,无需再改(因为\(D(2)=2,D(1)=1\))。
所以,使用两个线段树,在update中加一句if(mseg[p]<=2) return ;即可
想到这个思路则非常简单!
下面放个求因子个数的方法

ll calc(ll x){//求因子个数
	if(cnt.count(x)) return cnt[x];
	ll ans=1;
	for(ll i=2;i*i<=x;++i){
		if(x%i==0){
			ll c=0;
			while(x%i==0){
				++c;
				x/=i;
			}
			ans*=c+1;
		}
	}
	if(x!=1) ans*=2;
	return ans;
}
//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
与前面那道开方的题比较类似?
*/
const int maxm=3e5+5,inf=0x3f3f3f3f,mod=998244353;
ll n,m,a[maxm],seg[maxm<<2],mseg[maxm<<2];
map<ll,ll> cnt;

int ls(int p) { return p<<1; }
int rs(int p) { return p<<1|1; }

void push_up(int p){
	seg[p]=seg[ls(p)]+seg[rs(p)];
	mseg[p]=max(mseg[ls(p)],mseg[rs(p)]);
	return ; 
}

void build(int p,int pl,int pr){
	if(pl==pr) {
		mseg[p]=seg[p]=a[pl];
		return ;
	}
	int mid=(pl+pr)>>1;
	build(ls(p),pl,mid);
	build(rs(p),mid+1,pr);
	push_up(p);
	return  ;
}

ll calc(ll x){//求因子个数
	if(cnt.count(x)) return cnt[x];
	ll ans=1;
	for(ll i=2;i*i<=x;++i){
		if(x%i==0){
			ll c=0;
			while(x%i==0){
				++c;
				x/=i;
			}
			ans*=c+1;
		}
	}
	if(x!=1) ans*=2;
	return ans;
}

void update(int l,int r,int p,int pl,int pr){
	// if(pr-pl+1>=seg[p]) return ;这句话写假了,因为D(2)=2!!!
	if(mseg[p]<=2) return ;
	if(pl==pr){
		mseg[p]=seg[p]=calc(seg[p]);
		return ;
	}
	int mid=(pl+pr)>>1;
	if(l<=mid) update(l,r,ls(p),pl,mid);
	if(mid<r) update(l,r,rs(p),mid+1,pr);
	push_up(p);
	return ;
}

ll query(int l,int r,int p,int pl,int pr){
	if(l<=pl && pr<=r) return seg[p];
	int mid=(pl+pr)>>1;
	ll ans=0;
	if(l<=mid) ans+=query(l,r,ls(p),pl,mid);
	if(mid<r) ans+=query(l,r,rs(p),mid+1,pr);
	return ans;
}

void solve(){
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		cin>>a[i];
	}
	build(1,1,n);
	ll c,x,y,ans;
	while(m--){
		cin>>c>>x>>y;
		if(c==1){//update
			update(x,y,1,1,n);
		}else{//query
			cout<<query(x,y,1,1,n)<<'\n';
		}
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
	// cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

K 题

cf 438 D. The Child and Sequence
题意:
给你长度为n的数组,共进行m次操作。每次操作要么求给定区间[l,r]的和;要么对于区间[l,r],对该区间内所有的i,均进行\(a[i]=a[i]\%x\)的修改;要么令\(a[k]=x\)
思路:
与J题类似,依旧是双线段树,一个维护区间和,为了query区间和;一个维护区间最大值,为了在进行操作2时,剪枝,就是当所在区间的最大值小于x时,就不需要进行取余操作了
另外就是标记一点,不出意外的话,本题直接利用tag标记取余可能不太行嗷,因为假设三个数1,2,5,对于3取余后求和,和为5,但是利用tag,对于和取余,结果为2。所以不太行。(这个是自己一开始的思路,后面发现错了)

//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
双线段树维护
*/
const int maxm=1e5+5,inf=0x3f3f3f3f,mod=998244353;
ll n,m,a[maxm],seg[maxm<<2],mseg[maxm<<2];

int ls(int p){ return p<<1; }
int rs(int p){ return p<<1|1; }

void push_up(int p){
	seg[p]=seg[ls(p)]+seg[rs(p)];
	mseg[p]=max(mseg[ls(p)],mseg[rs(p)]);
	return ;
}

void build(int p,int pl,int pr){
	if(pl==pr){
		mseg[p]=seg[p]=a[pl];
		a[pl]=p;
		return ;
	}
	int mid=(pl+pr)>>1;
	build(ls(p),pl,mid);
	build(rs(p),mid+1,pr);
	push_up(p);
	return ;
}

void update(int pos,ll k,int p,int pl,int pr){
	if(pl==pr){
		mseg[p]=seg[p]=k;
		return ;
	}
	int mid=(pl+pr)>>1;
	if(pos<=mid) update(pos,k,ls(p),pl,mid);
	else update(pos,k,rs(p),mid+1,pr);
	push_up(p);
	return ;
}

void modd(int l,int r,ll k,int p,int pl,int pr){
	if(mseg[p]<k) return ;
	if(pl==pr){
		seg[p]%=k;
		mseg[p]=seg[p];
		return ;
	}
	int mid=(pl+pr)>>1;
	if(l<=mid) modd(l,r,k,ls(p),pl,mid);
	if(mid<r) modd(l,r,k,rs(p),mid+1,pr);
	push_up(p);
	return ;
}

ll query(int l,int r,int p,int pl,int pr){
	if(l<=pl&&pr<=r) return seg[p];
	ll ans=0;
	int mid=(pl+pr)>>1;
	if(l<=mid) ans+=query(l,r,ls(p),pl,mid);
	if(mid<r) ans+=query(l,r,rs(p),mid+1,pr);
	return ans;
}

void solve(){
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		cin>>a[i];
	}
	build(1,1,n);
	ll c,x,y,k;
	while(m--){
		cin>>c>>x>>y;
		if(c==1){//query
			cout<<query(x,y,1,1,n)<<'\n';
		}else if(c==2){//mod
			cin>>k;
			modd(x,y,k,1,1,n);
		}else{//update a[k]=x;
			update(x,y,1,1,n);
		}
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

L 题

cf 1187 D. Subarray Sorting
题意:
给你两个长度均为n的数组a和数组b,你可以对数组a执行任意次指定的操作——对a的某一个区间排序成非降序顺序。问你,能否通过操作,使得a数组变为b数组?
思路:
尽管是线段树专题,但乍一眼,看不到与线段树有任何的关系。。。那就得分析题目的意思,之后判断怎么用线段树
首先,a数组和b数组的所包含的元素种类和个数必须相同,不然无可能
其次,对于b中一个元素,都是由a中通过操作得到的。如果说a与b对应位置已经相等,那么这个位置就不用管了,因为已经符合条件了,或者说这个位置就可以不用考虑了。
那么我们从头开始看b数组,若相等,则删去当前位置的影响(怎么删等会说),考虑下一个位置;若不等,则我们需要找到a中第一个和它相等的位置pos(为啥不是最后一个或者别的?...题目问的是成立性!第一个最有可能成立,此处不多赘述),再移过来。因为我们是从前往后找的,前面的都是符合条件了的,这就相当于将其新的符合要求的数挪到a数组的开头,这个开头可以看作,一个动态的数组a的开头已经符合条件的数都已经被删去了。而能否移到开头?通过题目的定义我们可以知道,最小的数才能挪到开头,所以我们利用线段树维护区间最小值即可。那么已经挪过的数怎么取消影响呢?将其线段树内单点修改为极大值即可,这样就不会影响了。
这题主要是思路,有了做题的思路,那么就简单了!

//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
排序肯定不是关键,主要是判断,b数组中的数能不能由a数组中的数移过来
线段树维护区间最小值,想法想假了,仅判断最小值不够用
*/
const int maxm=3e5+5,inf=0x3f3f3f3f,mod=998244353;
int n,a[maxm],seg[maxm<<2],b[maxm];
deque<int> q[maxm];

int ls(int p){return p<<1;}
int rs(int p){return p<<1|1;}
 
void push_up(int p){//更新seg
	seg[p]=min(seg[ls(p)],seg[rs(p)]);
	return ;
}
 
void build(int p,int pl,int pr){//建树
	if(pl==pr){
		seg[p]=a[pl];
		return ;
	}
	int mid=(pl+pr)>>1;
	build(ls(p),pl,mid);
	build(rs(p),mid+1,pr);
	push_up(p);
	return ;
}

void update(int pos,ll k,int p,int pl,int pr){
	if(pl==pr){
		seg[p]=k;
		return ;
	}
	int mid=(pl+pr)>>1;
	if(pos<=mid) update(pos,k,ls(p),pl,mid);
	else update(pos,k,rs(p),mid+1,pr);
	push_up(p);
	return ;
}
 
ll query(int l,int r,int p,int pl,int pr){
	if(l<=pl&&pr<=r) return seg[p];
	int mid=(pl+pr)>>1;
	ll res=inf;
	if(l<=mid) res=min(res,query(l,r,ls(p),pl,mid));
	if(mid<r) res=min(res,query(l,r,rs(p),mid+1,pr));
	return res;
}

void solve(){
	cin>>n;
	for(int i=1;i<=n;++i){
		q[i].clear();
	}
	for(int i=1;i<=n;++i){
		cin>>a[i];
		q[a[i]].push_back(i);
	}
	build(1,1,n);
	bool f=true;
	int t,pos,dm;
	for(int i=1;i<=n;++i){
		cin>>b[i];
	}
	for(int i=1;i<=n;++i){
		t=b[i];
		if(!f) continue;
		if(q[t].size()){
			pos=q[t].front();
			q[t].pop_front();
			dm=query(1,pos,1,1,n);
			if(dm<t){
				f=false;
				continue;
			}
			update(pos,inf,1,1,n);
		}else{
			f=false;
		}
	}
	if(f) cout<<"YES\n";
	else cout<<"NO\n";
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

M 题

cf 380 C. Sereja and Brackets
题意:
给你一个括号序列,再进行m次询问,每次询问一个区间[l,r]内的括号匹配的最大数量
思路:
参考本博客的H题和I题,思路大体一致。
怎么说呢这题,其实,答案在给定括号序列的时候已经确定了,我们要做的就是如何确定答案。
我们先考虑括号与括号间的关系:"()","((","))",")(",四种情况第一种匹配成功,二三种同左同右,第四种左右都有但是顺序不对。那么,括号数再多一点呢?
以"(())))(())..."与")))(()))))..."这俩为例,他俩合并后,整个序列的匹配数是不是等于左边的匹配数+右边的匹配数+min(左边左括号未匹配数,右边右括号未匹配数)?,整个序列的未匹配括号数就是剩下的括号数。既然如此,那么我们是不是可以从每个字符出发构造线段树?为每个区间设置3个参数:ans,ml,mr,分别标识匹配数,左未匹配数,右未匹配数。
那么!线段树的建立过程就很明显了,仅修改push_up()函数即可

void push_up(int p){
	int d=min(seg[ls(p)].ml,seg[rs(p)].mr);
	seg[p].ans=seg[ls(p)].ans+seg[rs(p)].ans+d*2;
	seg[p].ml=seg[ls(p)].ml+seg[rs(p)].ml-d;
	seg[p].mr=seg[ls(p)].mr+seg[rs(p)].mr-d;
	return ;
}

查询操作与之对应!返回node,答案即为node x.ans
下为代码:

//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
与之前做的一道询问区间的查询连续子区间最大和的题目类似
*/
const int maxm=1e6+5,inf=0x3f3f3f3f,mod=998244353;
string ss;
int n,m;

struct node{
	int ans,ml,mr;
}seg[maxm<<2];

int ls(int p) {return p<<1;}
int rs(int p) {return p<<1|1;}

void push_up(int p){
	int d=min(seg[ls(p)].ml,seg[rs(p)].mr);
	seg[p].ans=seg[ls(p)].ans+seg[rs(p)].ans+d*2;
	seg[p].ml=seg[ls(p)].ml+seg[rs(p)].ml-d;
	seg[p].mr=seg[ls(p)].mr+seg[rs(p)].mr-d;
	return ;
}

void build(int p,int pl,int pr){
	if(pl==pr){
		seg[p].ans=seg[p].ml=seg[p].mr=0;
		if(ss[pl-1]=='(') seg[p].ml=1;
		else seg[p].mr=1;
		return ;
	}
	int mid=(pl+pr)>>1;
	build(ls(p),pl,mid);
	build(rs(p),mid+1,pr);
	push_up(p);
	return ;
}

node query(int l,int r,int p,int pl,int pr){
	if(l<=pl&&pr<=r) return seg[p];
	int mid=(pl+pr)>>1;
	if(r<=mid) return query(l,r,ls(p),pl,mid);
	else if(l>mid) return query(l,r,rs(p),mid+1,pr);
	else{
		node t,x=query(l,r,ls(p),pl,mid),y=query(l,r,rs(p),mid+1,pr);
		int d=min(x.ml,y.mr);
		t.ans=x.ans+y.ans+d*2;
		t.ml=x.ml+y.ml-d;
		t.mr=x.mr+y.mr-d;
		return t;
	}
}

void solve(){
	cin>>ss;
	n=ss.size();
	build(1,1,n);
	cin>>m;
	int l,r;
	while(m--){
		cin>>l>>r;
		cout<<query(l,r,1,1,n).ans<<'\n';
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

题意:

思路:

posted on 2023-07-04 09:35  Qiansui  阅读(16)  评论(0编辑  收藏  举报