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)
- 记得取模

浙公网安备 33010602011771号