题目分析
听说是很典的一道题,很明显难点在于除法下取整的操作。
类似花神那一道题,但是由于有区间加,所以无法进行暴力修改。
很明显暴力复杂度爆炸,考虑下取整带来的性质:
- 对于一对相邻的数,很明显有 \(\lfloor \frac{x-1}{k}\rfloor \le \lfloor \frac{x}{k}\rfloor -1\)。
- 我们将上面的柿子稍微做一点变形,可以得到:$x-1-\lfloor \frac{x-1}{k}\rfloor \le x-\lfloor \frac{x}{k}\rfloor $。
可见对于一个函数 \(f(x,k)=x-\lfloor \frac{x}{k}\rfloor\),是单调递增的,所以当一个区间的最大值和最小值的函数值相等时,相当于区间同时减掉同一个数(其实就是这个函数的定义),上线段树维护即可。
可以用势能分析,时间复杂度大约为 \(O(n\log n)\)。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
inline int read(){
int 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;
}
const int maxn=1e5+20;
int n,a[maxn],q;
struct node{
ll sum,tag1,mn,mx;
}tree[maxn<<2];
struct segment_tree{
inline int ls(int x){
return x<<1;
}
inline int rs(int x){
return x<<1|1;
}
inline void push_up(int p){
tree[p].sum=tree[ls(p)].sum+tree[rs(p)].sum;
tree[p].mn=min(tree[ls(p)].mn,tree[rs(p)].mn);
tree[p].mx=max(tree[ls(p)].mx,tree[rs(p)].mx);
}
inline void push_down(int p,int l,int r){
int mid=l+r>>1;
tree[ls(p)].mn+=tree[p].tag1,tree[rs(p)].mn+=tree[p].tag1;
tree[ls(p)].mx+=tree[p].tag1,tree[rs(p)].mx+=tree[p].tag1;
tree[ls(p)].sum+=tree[p].tag1*(mid-l+1),tree[rs(p)].sum+=tree[p].tag1*(r-mid);
tree[ls(p)].tag1+=tree[p].tag1,tree[rs(p)].tag1+=tree[p].tag1;
tree[p].tag1=0;
}
void build(int p,int l,int r){
tree[p].tag1=0;
if(l==r){
tree[p].sum=tree[p].mn=tree[p].mx=a[l];
return;
}
int mid=l+r>>1;
build(ls(p),l,mid);
build(rs(p),mid+1,r);
push_up(p);
}
void update1(int p,int l,int r,ll x,ll y,ll k){
if(x<=l&&r<=y){
tree[p].mn+=k,tree[p].mx+=k,tree[p].sum+=1ll*k*(r-l+1),tree[p].tag1+=k;
return;
}
push_down(p,l,r);
int mid=l+r>>1;
if(x<=mid) update1(ls(p),l,mid,x,y,k);
if(y>mid) update1(rs(p),mid+1,r,x,y,k);
push_up(p);
}
void update2(int p,int l,int r,int x,int y,int k){
if(x<=l&&r<=y&&(ll)floor(1.0*tree[p].mx/k)-tree[p].mx==(ll)floor(1.0*tree[p].mn/k)-tree[p].mn){
tree[p].tag1+=(ll)floor(1.0*tree[p].mn/k)-tree[p].mn;
tree[p].sum=tree[p].sum+((ll)floor(1.0*tree[p].mn/k)-tree[p].mn)*(r-l+1),tree[p].mn=(ll)floor(tree[p].mn*1.0/k),tree[p].mx=(ll)floor(tree[p].mx*1.0/k);
return;
}
push_down(p,l,r);
int mid=l+r>>1;
if(x<=mid) update2(ls(p),l,mid,x,y,k);
if(y>mid) update2(rs(p),mid+1,r,x,y,k);
push_up(p);
}
ll query1(int p,int l,int r,int x,int y){
if(x<=l&&r<=y) return tree[p].mn;
push_down(p,l,r);
int mid=l+r>>1;
if(y<=mid) return query1(ls(p),l,mid,x,y);
if(x>mid) return query1(rs(p),mid+1,r,x,y);
return min(query1(ls(p),l,mid,x,y),query1(rs(p),mid+1,r,x,y));
}
ll query2(int p,int l,int r,int x,int y){
if(x<=l&&r<=y) return tree[p].sum;
push_down(p,l,r);
int mid=l+r>>1;
if(y<=mid) return query2(ls(p),l,mid,x,y);
if(x>mid) return query2(rs(p),mid+1,r,x,y);
return query2(ls(p),l,mid,x,y)+query2(rs(p),mid+1,r,x,y);
}
}S;
signed main(){
n=read(),q=read();
for(int i=0;i<n;i++) a[i+1]=read();
S.build(1,1,n);
int op,l,r,x;
while(q--){
op=read(),l=read(),r=read();
l++,r++;
if(op==1) x=read(),S.update1(1,1,n,l,r,x);
else if(op==2) x=read(),S.update2(1,1,n,l,r,x);
else if(op==3) printf("%lld\n",S.query1(1,1,n,l,r));
else printf("%lld\n",S.query2(1,1,n,l,r));
}
return 0;
}
/*
*/
标签:tag1,rs,int,题解,tree,mid,Day1,雅礼,ls
From: https://www.cnblogs.com/dayz-break/p/18432317