这是一个悲伤的故事
别人在博弈论,我在哲学家;
别人在学数学,我在哲学家;
别人在吉司机,我在哲学家;
别人在剩余系,我在哲学家;
别人在改考试题,我 TM 还在哲学家;

我怀疑这道题叫哲学家,就是因为你写完后,会对人生有一种船新的思考。

首先,乘积的十进制最高位,可以就把每个数都取 log,这样乘就变成了加,就不会大到爆炸。然后提取了和之后,去掉整数部分,pow 一下就能得到十进制最高位了。

原序列看做若干权值线段树,一棵权值线段树里维护的元素是有序的,至于是升序还是降序,打个标记就好了。然后每一次操作,我们首先要将 l 和 r 所在的线段树分裂一下,这样若干棵权值线段树就构成了我们的操作区间,若是修改操作,就将这些线段树合并,是询问就查询它们的元素和。

有一个关键的问题就是要随时维护这些权值线段树放的顺序,来保持它们的有序,所以需要一棵平衡树。

你会发现这题需要查询区间和,所以就不能用 set,必须手写 splay……QAQ

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
    int q=0;char ch=' ';
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
    return q;
}
const int N=200005;
typedef double db;
const db eps=1e-8;
typedef pair<int,int> PR;
int n,m,rt,last_ins,js,tg[N],p[N];

struct node{
    int ls,rs,sz;db sum;
    void clear() {ls=rs=sz=0,sum=0;}
};
struct segment_tree{
    int SZ;stack<int> rub;
    node tr[N*60];
    int newnode() {
        if(!rub.empty()) {int re=rub.top();rub.pop();return re;}
        else return ++SZ;
    }
    void up(int k) {
        tr[k].sum=tr[tr[k].ls].sum+tr[tr[k].rs].sum;
        tr[k].sz=tr[tr[k].ls].sz+tr[tr[k].rs].sz;
    }
    int ins(int x,int s,int t,int k,db v) {//插入
        if(!k) k=newnode();
        int mid=(s+t)>>1;
        if(s==t) {tr[k].sum+=v,++tr[k].sz;return k;}
        if(x<=mid) tr[k].ls=ins(x,s,mid,tr[k].ls,v);
        else tr[k].rs=ins(x,mid+1,t,tr[k].rs,v);
        up(k);return k;
    }
    int mix(int a,int b) {
        if(!a&&!b) return 0;
        int k=newnode(); tr[k].ls=a,tr[k].rs=b,up(k); return k;
    }
    PR split(int k,int sz_to_split) {//分裂
        if(!k) return (PR){0,0};
        if(tr[k].sz==sz_to_split) return (PR){k,0};
        if(!sz_to_split) return (PR){0,k};
        if(!tr[k].ls&&!tr[k].rs) {
            int a_new_node=newnode();
            tr[a_new_node].sum=tr[k].sum/(db)tr[k].sz*(db)sz_to_split;
            tr[a_new_node].sz=sz_to_split;
            tr[k].sz-=sz_to_split,tr[k].sum-=tr[a_new_node].sum;
            return (PR){a_new_node,k};
        }
        if(sz_to_split<=tr[tr[k].ls].sz) {
            PR re=split(tr[k].ls,sz_to_split);
            re=(PR){mix(re.first,0),mix(re.second,tr[k].rs)};
            tr[k].clear(),rub.push(k);
            return re;
        }
        else {
            PR re=split(tr[k].rs,sz_to_split-tr[tr[k].ls].sz);
            re=(PR){mix(tr[k].ls,re.first),mix(0,re.second)};
            tr[k].clear(),rub.push(k);
            return re;
        }
    }
    int merge(int x,int y) {//合并
        if(!x||!y) return x|y;
        tr[x].ls=merge(tr[x].ls,tr[y].ls);
        tr[x].rs=merge(tr[x].rs,tr[y].rs);
        tr[x].sz+=tr[y].sz,tr[x].sum+=tr[y].sum;
        tr[y].clear(),rub.push(y);
        return x;
    }
}T;

struct nico{
    int l,r,id,s[2],f;db sum,v;
    void clear() {sum=v=0;f=s[0]=s[1]=id=l=r=0;}
};
struct Splay{
    int SZ;stack<int> rub;
    nico tr[N];
    int newnode() {
        if(!rub.empty()) {int re=rub.top();rub.pop();return re;}
        else return ++SZ;
    }
    int is(int k) {return tr[tr[k].f].s[1]==k;}
    void up(int k) {tr[k].sum=tr[tr[k].s[0]].sum+tr[tr[k].s[1]].sum+tr[k].v;}
    void spin(int x,int &mb) {
        int fa=tr[x].f,g=tr[fa].f,t=is(x);
        if(fa==mb) mb=x;
        else tr[g].s[is(fa)]=x;
        tr[x].f=g,tr[fa].f=x,tr[tr[x].s[t^1]].f=fa;
        tr[fa].s[t]=tr[x].s[t^1],tr[x].s[t^1]=fa;
        up(fa),up(x);
    }
    void splay(int x,int &mb) {
        while(x!=mb) {
            if(tr[x].f!=mb) {
                if(is(x)^is(tr[x].f)) spin(x,mb);
                else spin(tr[x].f,mb);
            }
            spin(x,mb);
        }
    }
    void ins(int &k,int fa,int L,int R,int id,db v) {//插入
        if(!k) {
            k=last_ins=newnode();
            tr[k].l=L,tr[k].r=R,tr[k].id=id,tr[k].f=fa;
            tr[k].sum=tr[k].v=v;
            return;
        }
        if(L<tr[k].l) ins(tr[k].s[0],k,L,R,id,v);
        else ins(tr[k].s[1],k,L,R,id,v);
        up(k);
    }
    int find(int k,int wz) {//查找
        if(tr[k].l<=wz&&wz<=tr[k].r) return k;
        if(tr[k].r<=wz) return find(tr[k].s[1],wz);
        else return find(tr[k].s[0],wz);
    }
    void del(int k) {//删除
        splay(k,rt);
        if(!tr[k].s[0]||!tr[k].s[1]) rt=tr[k].s[0]|tr[k].s[1],tr[rt].f=0;
        else {
            int x=tr[k].s[1];
            while(tr[x].s[0]) x=tr[x].s[0];
            tr[x].s[0]=tr[k].s[0],tr[tr[k].s[0]].f=x;
            rt=tr[k].s[1],tr[rt].f=0;
            while(x) up(x),x=tr[x].f;
        }
        tr[k].clear(),rub.push(k);
    }
}S;

void Ins(int l,int r,int k)
    {S.ins(rt,0,l,r,k,T.tr[k].sum),S.splay(last_ins,rt);}
void QAQ(int l,int r) {
    int k1=S.find(rt,l),tmp_tg1=tg[k1];nico nico1=S.tr[k1];
    if(nico1.l<l) {
        S.del(k1);
        int ksz=(tmp_tg1?l-nico1.l:nico1.r-l+1);
        PR tmp_pair=T.split(nico1.id,ksz);
        if(!tmp_tg1) swap(tmp_pair.first,tmp_pair.second);//若是降序,小的值在后
        Ins(nico1.l,l-1,tmp_pair.first),tg[last_ins]=tmp_tg1;//记得维护升序降序标记
        Ins(l,nico1.r,tmp_pair.second),tg[last_ins]=tmp_tg1;
    }
    int k2=S.find(rt,r),tmp_tg2=tg[k2];nico nico2=S.tr[k2];
    if(nico2.r>r) {
        S.del(k2);
        int ksz=(tmp_tg2?r-nico2.l+1:nico2.r-r);
        PR tmp_pair=T.split(nico2.id,ksz);
        if(!tmp_tg2) swap(tmp_pair.first,tmp_pair.second);
        Ins(nico2.l,r,tmp_pair.first),tg[last_ins]=tmp_tg2;
        Ins(r+1,nico2.r,tmp_pair.second),tg[last_ins]=tmp_tg2;
    }
    S.splay(S.find(rt,l-1),rt),S.splay(S.find(rt,r+1),S.tr[rt].s[1]);
    //套路,将区间放在根的右儿子的左子树
}
void dfs(int x) {
    if(S.tr[x].s[0]) dfs(S.tr[x].s[0]);
    if(S.tr[x].s[1]) dfs(S.tr[x].s[1]);
    p[++js]=S.tr[x].id,S.tr[x].clear(),S.rub.push(x);
}
int main()
{
    int op,l,r,x,k;
    n=read(),m=read();
    Ins(0,0,0),Ins(n+1,n+1,0);
    for(RI i=1;i<=n;++i)
        x=read(),k=T.ins(x,1,n,0,log10((db)x)),Ins(i,i,k);
    while(m--) {
        op=read(),l=read(),r=read();
        QAQ(l,r);
        int x=S.tr[S.tr[rt].s[1]].s[0];
        if(op==1) {
            js=0,dfs(x),k=p[1];
            S.tr[S.tr[rt].s[1]].s[0]=0;
            for(RI i=2;i<=js;++i) k=T.merge(k,p[i]);
            Ins(l,r,k),tg[last_ins]=read();
        }
        else {
            db ans=S.tr[x].sum;
            ans-=floor(ans+eps),ans=pow(10,ans);
            printf("%d\n",(int)floor(ans+eps)%10);
        }
    }
    return 0;
}
分类: 文章

litble

苟...苟活者在淡红的血色中,会依稀看见微茫的希望

1 条评论

Remmina · 2019年1月15日 7:47 下午

哲♂学家 litble

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注