BZOJ 3323 SCOI2013 多项式的运算 splay

Problem

Description

某天,mzry1992 一边思考着一个项目问题一边在高速公路上骑着摩托车。一个光头踢了他一脚,摩托车损坏,而他也被送进校医院打吊针。现在该项目的截止日期将近,他不得不请你来帮助他完成这个项目。
该项目的目的是维护一个动态的关于x 的无穷多项式 ,这个多项式初始时对于所有i有 ai=0 a i = 0
f(x)=a0x0+a1x1+a2x2... f ( x ) = a 0 x 0 + a 1 x 1 + a 2 x 2 . . .

操作者可以进行四种操作:
xLxLxL x L x L x L xRxRxR x R x R x R 这些项的系数乘上某个定值v
xLxLxL x L x L x L xRxRxR x R x R x R 这些项的系数加上某个定值v
xL x L xR x R 这些项乘上x变量
将某个定值 v v 代入多项式f(x),并输出代入后多项式的值,之后多项式还原为代入前的状况

经过观察,项目组发现使用者的操作集中在前三种,第四种操作不会出现超过10次。mzry1992 负责这个项目的核心代码,你能帮他实现么?

Input

输入的第一行有一个整数n,代表操作的个数。
接下来n行,每行一个操作,格式如下:
mul L R v 代表第一种操作
add L R v 代表第二种操作
mulx L R 代表第三种操作
query v 代表第四种操作

Output

对于每个query 操作,输出对应的答案,结果可能较大,需要模上20130426。

Sample

Input
6
add 0 1 7
query 1
mul 0 1 7
query 2
mulx 0 1
query 3
Output
14
147
588

Hint

操作一之后,多项式为 f(x)=7x+7 f ( x ) = 7 x + 7
操作三之后,多项式为 f(x)=49x+49 f ( x ) = 49 x + 49
操作五之后,多项式为 f(x)=49x2+49x f ( x ) = 49 x 2 + 49 x

Date Size

对于30% 的数据: N50000LR50000v109 N ≤ 5000 , 0 ≤ L ≤ R ≤ 5000 , 0 ≤ v ≤ 10 9
另有20% 的数据: N1050LR1050v109 N ≤ 10 5 , 0 ≤ L ≤ R ≤ 10 5 , 0 ≤ v ≤ 10 9 ,没有mulx 操作
剩下的50% 的数据: N1050LR1050v109 N ≤ 10 5 , 0 ≤ L ≤ R ≤ 10 5 , 0 ≤ v ≤ 10 9

Solution

其他

话说我写这个题写了四天,简直就是调试神题,还扒了boshi写的题解下来对拍(嗯…可以附赠小数据生成器)。直到最后不知道怎么改了了,重构代码才A掉。
发现原来的程序错了两个地方。一个是在mulx操作时下推标记的fr和ba没有下推标记,另一个是新建节点还是比较多,开给节点的空间少了,应该至少要开到2e5。讲讲解题的思路吧。

方法

用splay维护系数,然后以节点在splay中的rank当做节点的幂。在对区间[l,r]进行操作时,转化为开区间(l-1,r+1),将l-1旋至根,r+1旋至l-1的右儿子,就可以使得操作区间到r+1节点的左子树中,然后就运用懒惰标记的思想。当然为了防止转化区间时溢出,将整个区间右移两位即可。

对于add,mul操作,直接更改r的左儿子然后打上懒惰标记即可,注意下推标记的时候先乘再加。

对于mulx操作的处理比较巧妙,需要把当前r幂的系数与r+1幂的系数进行合并,然后新建立代表l幂系数为0的节点。这里用fr代表l幂的节点,ba代表r幂的节点。在操作之前,要将根节点到fr、ba的路径上的所有标记下推。
然后删除ba。在删除的时候,把ba的左子树接到父亲节点上去。可以考虑到,当ba的父亲节点为r时,其左子树应接在父亲节点的左儿子上,其他情况接右儿子上。最后然后找到fr,并为其新建左儿子。
但为了保证下次操作findx函数的正确性,在操作完之后,要对根节点(r节点亦可)到fr、ba的链上的节点进行pushup操作。

query操作因为不超过10次,可以利用 O(n) O ( n ) 的dfs出所有系数,然后暴力完成询问。

Code

#include <iostream>
#include <cstdio>
#define pd(x) if(add[x]||mul[x]!=1)pushdown(x)
//据说像这样在外面判断一下懒惰标记可以减小常数
using namespace std;
typedef long long ll;
const int maxn=200000,mod=20130426;
int n,cnt,rt,sz,l,r,fr,ba,f[maxn],ch[maxn][2],s[maxn];
ll v,ans,key[maxn],add[maxn],mul[maxn],tmp[maxn];
char str[10];
inline void clear(int x){f[x]=ch[x][0]=ch[x][1]=s[x]=0;key[x]=add[x]=mul[x]=0;}
inline void pushup(int x){s[x]=s[ch[x][0]]+s[ch[x][1]]+1;}
inline void pushdown(int x)
{
    int l=ch[x][0],r=ch[x][1];
    if(mul[x]!=1)
    {
        if(l) key[l]=(key[l]*mul[x])%mod,mul[l]=(mul[l]*mul[x])%mod,add[l]=(add[l]*mul[x])%mod;
        if(r) key[r]=(key[r]*mul[x])%mod,mul[r]=(mul[r]*mul[x])%mod,add[r]=(add[r]*mul[x])%mod;
        mul[x]=1;
    }
    if(add[x])
    {
        if(l) key[l]=(key[l]+add[x])%mod,add[l]=(add[l]+add[x])%mod;
        if(r) key[r]=(key[r]+add[x])%mod,add[r]=(add[r]+add[x])%mod;
        add[x]=0;
    }
}
void rotate(int x,int &k)
{
    int fa=f[x],ff=f[fa],l,r;
    if(ch[fa][0]==x) l=0;
    else l=1;r=l^1;
    if(fa==k) k=x;
    else {if(ch[ff][0]==fa) ch[ff][0]=x;else ch[ff][1]=x;}
    f[x]=ff;f[fa]=x;f[ch[x][r]]=fa;
    ch[fa][l]=ch[x][r];ch[x][r]=fa;
    pushup(fa);pushup(x);
}
void splay(int x,int &k)
{
    while(x!=k)
    {
        int fa=f[x],ff=f[fa];
        if(fa!=k)
        {
            if(ch[fa][0]==x^ch[ff][0]==fa)
              rotate(x,k);
            else
              rotate(fa,k);
        }
        rotate(x,k);
    }
}
inline int build(int l,int r,int pre)//直接建所有节点
{
    if(l>r) return 0;
    int m=(l+r)>>1;
    sz++;clear(m);
    f[m]=pre;mul[m]=1;
    ch[m][0]=build(l,m-1,m);
    ch[m][1]=build(m+1,r,m);
    pushup(m);
    return m;
}
int findx(int x)//find the kth node
{
    int now=rt;
    while(1)
    {
        if(add[now]||mul[now]!=1) pushdown(now);
        if(ch[now][0]&&x<=s[ch[now][0]]) now=ch[now][0];
        else
        {
            int t=(ch[now][0]?s[ch[now][0]]:0)+1;
            if(x<=t) return now;
            x-=t;now=ch[now][1];
        }
    }
}
void work1(int l,int r,ll v)
{
    int now;
    l=findx(l-1);r=findx(r+1);
    splay(l,rt);splay(r,ch[l][1]);now=ch[r][0];
    pd(now);
    key[now]=(key[now]+v)%mod;add[now]=(add[now]+v)%mod;
}
void work2(int l,int r,ll v)
{
    int now;
    l=findx(l-1);r=findx(r+1);
    splay(l,rt);splay(r,ch[l][1]);now=ch[r][0];
    pd(now);
    key[now]=(key[now]*v)%mod;mul[now]=(mul[now]*v)%mod;
}
void work3(int l,int r)
{
    l=findx(l-1);r=findx(r+1);
    splay(l,rt);splay(r,ch[l][1]);
    pd(l);pd(r);
    ba=ch[r][0];
    while(ch[ba][1]){pd(ba);ba=ch[ba][1];}
    pd(ba);key[r]+=key[ba];
    if(f[ba]==r) ch[f[ba]][0]=ch[ba][0],f[ch[ba][0]]=f[ba];
    else ch[f[ba]][1]=ch[ba][0],f[ch[ba][0]]=f[ba];
    while(ba!=r){ba=f[ba];pushup(ba);}//clear(ba);
    fr=r;
    while(ch[fr][0]){pd(fr);fr=ch[fr][0];}
    pd(fr);ch[fr][0]=++sz;clear(sz);
    f[sz]=fr;mul[sz]=1;s[sz]=1;fr=sz;
    while(fr!=r){fr=f[fr];pushup(fr);}
}
void dfs(int now,int pos)
{
    pd(now);
    if(s[ch[now][0]]+1>pos) return ;
    if(ch[now][0]) dfs(ch[now][0],pos);
    tmp[++cnt]=key[now];
    if(ch[now][1]) dfs(ch[now][1],pos-s[ch[now][0]]-1);
}
void query(ll v)
{
    ll x=1;
    ans=0;cnt=0;
    dfs(rt,100100);
    for(int i=2;i<cnt;i++) ans=(ans+tmp[i]*x)%mod,x=x*v%mod;
    printf("%lld\n",ans);
}
int main()
{
    scanf("%d",&n);
    rt=build(1,100100,0);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",str);
        if(str[0]=='a'){scanf("%d%d%lld",&l,&r,&v);work1(l+2,r+2,v);}
        if(str[0]=='m'&&str[3]!='x'){scanf("%d%d%lld",&l,&r,&v);work2(l+2,r+2,v);}
        if(str[0]=='m'&&str[3]=='x'){scanf("%d%d",&l,&r);work3(l+2,r+2);}
        if(str[0]=='q'){scanf("%lld",&v);query(v);}
    }
    return 0;
}

另附 数据生成器

#include <bits/stdc++.h>
using namespace std;
int main()
{
    srand(time(NULL));
    rand();
    int n=100,l,r,v,op;
    printf("%d\n",n);
    for(int i=1;i<n;i++)
    {
        op=rand()%4;
        l=rand()%6;r=rand()%6;v=rand()%3;
        if(l>r) swap(l,r);
        if(op==3) v=(v&1)+1;
        switch(op)
        {
            case 0:printf("mul %d %d %d\n",l,r,v);break;
            case 1:printf("add %d %d %d\n",l,r,v);break;
            case 2:printf("mulx %d %d\n",l,r);break;
            case 3:printf("query %d\n",v);break;
        }
    }
    printf("query %d\n",2);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值