1. 程式人生 > >【LOJ#6029】市場(線段樹)

【LOJ#6029】市場(線段樹)

【LOJ#6029】市場(線段樹)

題面

LOJ

題解

看著就是一個需要勢能分析的線段樹。
不難發現就是把第二個整除操作化為減法。
考慮一下什麼時候整除操作才能變成減法。
假設兩個數為\(a,b\)。那麼就有\(\displaystyle a-[\frac{a}{d}]=b-[\frac{b}{d}]\)
那麼假設\(a,b\)整除的結果分別為\(aa,bb\)\(a=d*aa+p_a,b=d*bb+p_b\)
得到:\(\displaystyle (d-1)aa+p_a=(d-1)bb+p_b\)
化簡後得到:\(\displaystyle (d-1)(aa-bb)=p_b-p_a\)


顯然\(p_a,p_b\)的取值範圍就是\([0,d)\),而左邊還乘了一個\(d-1\)。也就意味著這個限制是非常強的,假設\(aa>bb\)(等於就必定相等了,沒什麼好考慮的),那麼\(p_b-p_b\in[0,d)\)。左邊顯然只能取\(1\),右邊顯然只能去\(d-1\)。寫出來就是:\(a=d*aa,b=d*(aa-1)+d-1\)。所以我們得到\(a-b=1\)
因此維護區間最大值和最小值,如果最大值和最小值整除操作之後的差相等,那麼可以變為區間減法。否則暴力下放。
至於複雜度?聽\(zsy\)口胡了一下他的分析。單獨考慮一個線段樹節點,它最多被暴力訪問\(loga\)次。如果區間加法則重置這個\(log\)
。而每次區間加法最多影響\(logn\)個區間,也就是會重置\(logn\)個區間的訪問次數。而\(m\)次操作最多重置\(mlogn\)個區間,因此最多增加訪問次數是\(loga\)次。所以總的複雜度就是\(O((n+mlogn)loga)\)
感覺很對的樣子。
注意一下負數的向下取整。

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 100100
#define lson (now<<1)
#define rson (now<<1|1)
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
struct Node{ll v;int mn,mx,tag;}t[MAX<<2];
void pushup(int now)
{
    t[now].mn=min(t[lson].mn,t[rson].mn);
    t[now].mx=max(t[lson].mx,t[rson].mx);
    t[now].v=t[lson].v+t[rson].v;
}
void Build(int now,int l,int r)
{
    if(l==r){t[now].v=t[now].mn=t[now].mx=read();return;}
    int mid=(l+r)>>1;
    Build(lson,l,mid);Build(rson,mid+1,r);
    pushup(now);
}
void puttag(int now,int l,int r,int w)
{
    t[now].v+=(r-l+1)*w;
    t[now].tag+=w;t[now].mn+=w;t[now].mx+=w;
}
void pushdown(int now,int l,int r)
{
    if(!t[now].tag)return;
    int mid=(l+r)>>1;
    puttag(lson,l,mid,t[now].tag);
    puttag(rson,mid+1,r,t[now].tag);
    t[now].tag=0;
}
void Modify(int now,int l,int r,int L,int R,int w)
{
    if(L<=l&&r<=R){puttag(now,l,r,w);return;}
    int mid=(l+r)>>1;pushdown(now,l,r);
    if(L<=mid)Modify(lson,l,mid,L,R,w);
    if(R>mid)Modify(rson,mid+1,r,L,R,w);
    pushup(now);
}
int Div(int a,int d)
{
    if(a>=0)return a/d-a;
    if(a%d==0)return a/d-a;
    return a/d-1-a;
}
void ModifyDiv(int now,int l,int r,int L,int R,int d)
{
    if(L<=l&&r<=R)
        if(Div(t[now].mx,d)==Div(t[now].mn,d))
        {puttag(now,l,r,Div(t[now].mx,d));return;}
    int mid=(l+r)>>1;pushdown(now,l,r);
    if(L<=mid)ModifyDiv(lson,l,mid,L,R,d);
    if(R>mid)ModifyDiv(rson,mid+1,r,L,R,d);
    pushup(now);
}
ll Query(int now,int l,int r,int L,int R)
{
    if(L<=l&&r<=R)return t[now].v;
    int mid=(l+r)>>1;pushdown(now,l,r);
    ll ret=0;
    if(L<=mid)ret+=Query(lson,l,mid,L,R);
    if(R>mid)ret+=Query(rson,mid+1,r,L,R);
    return ret;
}
int Querymn(int now,int l,int r,int L,int R)
{
    if(L==l&&r==R)return t[now].mn;
    int mid=(l+r)>>1;pushdown(now,l,r);
    if(R<=mid)return Querymn(lson,l,mid,L,R);
    if(L>mid)return Querymn(rson,mid+1,r,L,R);
    return min(Querymn(lson,l,mid,L,mid),Querymn(rson,mid+1,r,mid+1,R));
}
int n,Q;
int main()
{
    n=read();Q=read();
    Build(1,1,n);
    while(Q--)
    {
        int opt=read(),l=read()+1,r=read()+1;
        if(opt==1)Modify(1,1,n,l,r,read());
        if(opt==2)ModifyDiv(1,1,n,l,r,read());
        if(opt==3)printf("%d\n",Querymn(1,1,n,l,r));
        if(opt==4)printf("%lld\n",Query(1,1,n,l,r));
    }
    return 0;
}