CSP-S模版-数据结构-1

模版来源于洛谷CSP-S模版赛

P1886 【模板】单调队列 / 滑动窗口

原题链接

题意

求出每个长度为 \(m\) 的区间内的小值和最大值

做法

滑动窗口八百年没写过差点忘了

其实我认为这篇应该在单调栈写完后写,所以先写单调栈

本质上比单调栈只多维护一个开头,因为要保证是在 \(m\) 个之内,所以说每当栈顶与 \(i\) 的距离超出的时候要 pop 掉
注意这里维护的东西左端点为答案,因为更新是从右向左,所以答案应该是在每次更新后的最左端

代码

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define int128 __int128
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3fll
#define finf 0xcfcfcfcf
#define FINF 0xcfcfcfcfcfcfcfcf
#define endl '\n'
#define lowbit(x) (x & -x)
#define ls (u<<1)
#define rs (u<<1|1)
#define eps 1e-9
#define mem(x) memset(x,0x3f,sizeof(x));
#define fmem(x) memset(x,0xcf,sizeof(x));
using namespace std;
const int maxn=1e6+5;
const int maxm=1e6;
const int mod=998244353;
int n,m,x,y,t;
int pw(int a,int b){

	int ans=1;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}

	return ans;
}
int a[maxn];
int st[maxn],l,r;
int ans[maxn];
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    l=1,r=0;
    for(int i=1;i<=n;i++){
		while(a[st[r]]>=a[i]&&l<=r)r--;
		while(l<=r&&st[l]+m<=i)l++;
		st[++r]=i;
		if(i>=m)cout<<a[st[l]]<<" ";
	}
	cout<<endl;
    l=1,r=0;
    for(int i=1;i<=n;i++){
		while(a[st[r]]<=a[i]&&l<=r)r--;
		while(l<=r&&st[l]+m<=i)l++;
		st[++r]=i;
		if(i>=m)cout<<a[st[l]]<<" ";
	}
	cout<<endl;
	
    
    return 0;
}

P5788 【模板】单调栈

原题链接

题意

求出下标大于 \(i\) 的数中第一个大于 \(a_i\) 的数的下标

做法

开一个栈存下标,如果栈顶的数小于等于当前的 \(a_i\),那就 pop 掉
这样操作直到栈顶大于当前数或栈里没数了为止
可以感性理解为把后面填平了,嗯对

代码

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define int128 __int128
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3fll
#define finf 0xcfcfcfcf
#define FINF 0xcfcfcfcfcfcfcfcf
#define endl '\n'
#define lowbit(x) (x & -x)
#define ls (u<<1)
#define rs (u<<1|1)
#define eps 1e-9
#define mem(x) memset(x,0x3f,sizeof(x));
#define fmem(x) memset(x,0xcf,sizeof(x));
using namespace std;
const int maxn=3e6+5;
const int maxm=1e6;
const int mod=998244353;
int n,m,x,y,t;
int pw(int a,int b){

	int ans=1;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}

	return ans;
}
int a[maxn];
int st[maxn],top;
int ans[maxn];
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=n;i>=1;i--){
		while(a[st[top]]<=a[i]&&top!=0)top--;
		ans[i]=st[top];
		st[++top]=i;
	}
	for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
	cout<<endl;
    
    return 0;
}

P3374 【模板】树状数组 1

原题链接

题意

区间加,区间求和

做法

树状数组
不怎么用吧,感觉除非线段树卡不过去了用用这个,功能远远少于线段树
\(t_x\) 记的是 \({x-lowbit(x)+1,x}\),由于 \(lowbit\) 很神奇或者说树状数组的构造很神奇,因此这个东西可以做到不重
虽然但是是按二进制来的,所以这个常数很小至少小于线段树

复杂度\(O((n+m)logn)\)

代码

我们似乎不把shitmountain称为好看

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define int128 __int128
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3fll
#define finf 0xcfcfcfcf
#define FINF 0xcfcfcfcfcfcfcfcf
#define endl '\n'
#define lowbit(x) (x & -x)
#define ls (u<<1)
#define rs (u<<1|1)
#define eps 1e-9
#define mem(x) memset(x,0x3f,sizeof(x));
#define fmem(x) memset(x,0xcf,sizeof(x));
using namespace std;
const int maxn=2e5+5;
const int maxm=1e6;
const int mod=998244353;
int n,m,x,y;
int pw(int a,int b){

	int ans=1;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}

	return ans;
}
int a[maxn];
int t[maxn];
void xg(int x,int d){
	for(int i=d;i<=n;i+=lowbit(i))t[i]+=d;
	return ;
}
int cx(int x){
	int sum=0;
	for(int i=x;i;i-=lowbit(i))sum+=t[i];
	return sum;
}
int query(int x,int y){
	return cx(y)-cx(x-1);
}
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
while(m--){
	int op;
	cin>>op;
	if(op==1){
		cin>>x>>y;
		xg(x,y);
	}
	else {
		cin>>x>>y;
		cout<<query(x,y)<<endl;
	}
}
    
    return 0;
}

附赠

卡常题· [yLOI2019] 棠梨煎雪

P3373 【模板】线段树 2

原题链接

题意

区间加,区间乘,区间求和

做法

线段树
这个东西本质上是一半一半的维护区间,也就是递归
每次分一半,复杂度log
这道题的话要注意 pushdown 先乘再加,乘也要乘给加标记,这样就不用考虑时间了

代码

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define int128 __int128
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3fll
#define finf 0xcfcfcfcf
#define FINF 0xcfcfcfcfcfcfcfcf
#define endl '\n'
#define lowbit(x) (x & -x)
#define ls (u<<1)
#define rs (u<<1|1)
#define eps 1e-9
#define mem(x) memset(x,0x3f,sizeof(x));
#define fmem(x) memset(x,0xcf,sizeof(x));
using namespace std;
const int maxn=2e5+5;
const int maxm=1e6;
//const int mod=998244353;
int n,m,x,y,t,q,mod;
int pw(int a,int b){

	int ans=1;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	

	return ans;
}
struct tree{
	int l,r,sum,lazy1,lazy2;
}w[maxn<<2];
int a[maxn];
void xz(int u){
	w[u].sum=w[ls].sum+w[rs].sum;
	return ;
}
bool pd(int l1,int r1,int l2,int r2){
	return (l1<=l2)&&(r2<=r1);
}
void pushdown(int u){
	if(w[u].lazy2!=1){
		w[ls].sum*=w[u].lazy2;w[ls].sum%=mod;
		w[rs].sum*=w[u].lazy2;w[rs].sum%=mod;
		w[ls].lazy1*=w[u].lazy2;w[ls].lazy1%=mod;
		w[rs].lazy1*=w[u].lazy2;w[rs].lazy1%=mod;
		w[ls].lazy2*=w[u].lazy2;w[ls].lazy2%=mod;
		w[rs].lazy2*=w[u].lazy2;w[rs].lazy2%=mod;
		w[u].lazy2=1;
	}
	if(w[u].lazy1){
		w[ls].sum+=(w[ls].r-w[ls].l+1)*w[u].lazy1;w[ls].sum%=mod;
		w[rs].sum+=(w[rs].r-w[rs].l+1)*w[u].lazy1;w[rs].sum%=mod;
		w[ls].lazy1+=w[u].lazy1;w[ls].lazy1%=mod;
		w[rs].lazy1+=w[u].lazy1;w[rs].lazy1%=mod;
		w[u].lazy1=0;
	}
	return ;
}
void build(int u,int l,int r){
	w[u].l=l,w[u].r=r;
	if(l==r){
		w[u].sum=a[l],w[u].lazy1=0,w[u].lazy2=1;
		return ;
	}
	int mid=(l+r)>>1;
	w[u].lazy2=1;
	build(ls,l,mid);build(rs,mid+1,r);
	xz(u);
}
void xg1(int u,int l,int r,int d){
	if(pd(l,r,w[u].l,w[u].r)){
		w[u].lazy1+=d;w[u].sum+=(w[u].r-w[u].l+1)*d;w[u].sum%=mod;
		return ;
	}
	pushdown(u);
	int mid=(w[u].l+w[u].r)>>1;
	if(l<=mid)xg1(ls,l,r,d);
	if(r>mid)xg1(rs,l,r,d);
	xz(u);
	return ;
}
void xg2(int u,int l,int r,int d){
	if(pd(l,r,w[u].l,w[u].r)){
		w[u].lazy2*=d;w[u].lazy2%=mod;w[u].lazy1*=d;w[u].lazy1%=mod;w[u].sum*=d;w[u].sum%=mod;
		return ;
	}
	pushdown(u);
	int mid=(w[u].l+w[u].r)>>1;
	if(l<=mid)xg2(ls,l,r,d);
	if(r>mid)xg2(rs,l,r,d);
	xz(u);
	return ;
}
int cx(int u,int l,int r){
	if(pd(l,r,w[u].l,w[u].r)){
		return w[u].sum;
	}
	pushdown(u);
	int mid=(w[u].l+w[u].r)>>1;
	int sum=0;
	if(l<=mid)sum+=cx(ls,l,r);
	if(r>mid)sum+=cx(rs,l,r);
	sum%=mod;
//	xz(u);
	return sum;
}
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>q>>mod;
    for(int i=1;i<=n;i++)cin>>a[i];
    build(1,1,n);
while(q--){
	int op;
	cin>>op;
	if(op==1){
		int x,y,k;
		cin>>x>>y>>k;
		xg2(1,x,y,k);
	}
	else if(op==2){
		int x,y,k;
		cin>>x>>y>>k;
		xg1(1,x,y,k);
	}
	else {
		int x,y;
		cin>>x>>y;
		cout<<cx(1,x,y)<<endl;
	}
}
    
    return 0;
}

警示自己

  • 乘标记要每一个区间都有,加标记不需要的原因是默认为0
  • 记得在 main 里写 \(build\)
  • build 的时候要给 \(w[u].l\) 和 \(w[u].r\) 赋值
  • 记得取模
  • 递归时要两边都判,而不是只判一遍然后另一边 else,这么写的是线段树上二分
    • if(l<=mid)
    • if(mid<r)
  • 记得取模

posted @ 2026-10-26 11:41  XTBT  阅读(9)  评论(0)    收藏  举报