bzoj 3217 ALOEXT 替罪羊树套trie树

感觉又刚了一遍带插入区间k小值。。。
这题写个替罪羊练一下。其实应该可以用重量平衡的treap套trie。

带插入维护一段区间的trie和次小值。
区间次小值随便维护,区间trie呢?
我会做!替罪羊套trie!
不过写到一半你会发现这题还有删除。

treap删点直接旋到下面删。那替罪羊怎么删?
直接重构?每次删中间的点就挂了。
打标记? 似乎会有很多特判。
只要像线段树一样建就好了。把所有的表示元素的点都放在叶节点,其他点都是空点。只维护子树中所有叶节点形成的trie。
插入时新建两个点,删除时直接删除叶节点。
查询时像线段树一样找到log个区间,然后类似主席树地跑。

#include <bits/stdc++.h>
using namespace std;
#define A 20
#define alp 0.93
#define N 1100000
#define M 35000000
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define which(x) (ch[fa[x]][1]==x)
int a[N],n,m,n0,ans;
int st[210],top;
char s[11];
struct Trie
{
    int ch[M][2],num[M],cnt;
    queue<int>q;
    int ap()
    {
        if(!q.empty())
        {
            int ret=q.front();q.pop();
            ls(ret)=rs(ret)=0;num[ret]=0;
            return ret;
        }
        return ++cnt;
    }
    void del(int x)
    {
        if(!x)return;
        q.push(x);
        del(ls(x));del(rs(x));
    }
    void insert(int &now,int v,int deep,int tp)
    {
        if(!now)now=ap();
        num[now]+=tp;
        if(deep==-1)return;
        if(v>>deep&1)insert(rs(now),v,deep-1,tp);
        else insert(ls(now),v,deep-1,tp);
    }
    int merge(int x,int y)
    {
        if(!x&&!y)return 0;
        int ret=ap();
        ls(ret)=merge(ls(x),ls(y));
        rs(ret)=merge(rs(x),rs(y));
        num[ret]=num[x]+num[y];
        return ret;
    }
    int query(int x,int deep)
    {
        if(deep<0)return 0;
        int t=x>>deep&1,sum=0;
        for(int i=1;i<=top;i++)
            sum+=num[ch[st[i]][t^1]];
        for(int i=1;i<=top;i++)
            st[i]=ch[st[i]][sum ? t^1:t];
        return query(x,deep-1)+(sum ? 1<<deep:0);
    }
}tr1;
struct node
{
    int mx,sx;
    node(){}
    node(int mx,int sx):mx(mx),sx(sx){}
    void ins(int x)
    {
        if(x>mx)sx=mx,mx=x;
        else if(x>sx)sx=x;
    }
};
node merge(node r1,node r2)
{
    node ret=r1;
    ret.ins(r2.mx);ret.ins(r2.sx);
    return ret;
}
struct Sheep
{
    queue<int>q;
    int ch[N][2],size[N],root[N],fa[N],val[N],sz[N],bj[N];
    int cnt,rt,fre,pre,tar;
    node v[N],n1;
    int ap()
    {
        if(!q.empty())
        {
            int t=q.front();q.pop();
            ls(t)=rs(t)=size[t]=root[t]=fa[t]=val[t]=bj[t]=0;
            v[t]=node(0,0);
            return t;
        }
        return ++cnt;
    }
    int newnode(int x)
    {
        int ret=ap();bj[ret]=1;
        val[ret]=x;size[ret]=sz[ret]=1;
        v[ret]=node(x,0);
        tr1.insert(root[ret],x,A,1);
        return ret;
    }
    void pushup(int x)
    {
        size[x]=size[ls(x)]+size[rs(x)];
        sz[x]=size[x]+1;
        root[x]=tr1.merge(root[ls(x)],root[rs(x)]);
        v[x]=merge(v[ls(x)],v[rs(x)]);
        fa[ls(x)]=fa[rs(x)]=x;
    }
    int build(int l,int r)
    {
        if(l>r)return 0;
        if(l==r)return newnode(a[l]);

        int mid=(l+r)>>1,ret=ap();
        ls(ret)=build(l,mid);
        rs(ret)=build(mid+1,r);
        pushup(ret);
        return ret;
    }
    void dfs(int x)
    {
        if(!x)return;
        q.push(x);tr1.del(root[x]);
        if(bj[x]){a[++n]=val[x];return;}
        dfs(ls(x));dfs(rs(x));
    }
    void reb(int x)
    {
        tr1.del(root[x]);
        n=0;dfs(ls(x));dfs(rs(x));
        int t=build(1,n);
        if(x==rt)rt=t;
        fa[t]=fa[x];
        ch[fa[x]][which(x)]=t;
    }
    void insert(int &now,int x,int y)
    {
        if(size[now]==1||!now)
        {
            int t=newnode(y),ret=ap();
            fa[ret]=pre;ls(ret)=t;rs(ret)=now;
            pushup(ret);now=ret;
            return;
        }
        pre=now;
        if(size[ls(now)]>=x)insert(ls(now),x,y);
        else insert(rs(now),x-size[ls(now)],y);

        tr1.insert(root[now],y,A,1);
        v[now].ins(y);size[now]++;sz[now]+=2;

        fa[ls(now)]=fa[rs(now)]=now;
        if(max(sz[ls(now)],sz[rs(now)])>sz[now]*alp)
            fre=now;
    }
    void del(int &now,int x)
    {
        if(bj[now])
        {q.push(now);tar=val[now];now=0;return;}

        if(size[ls(now)]>=x)del(ls(now),x);
        else del(rs(now),x-size[ls(now)]);

        tr1.insert(root[now],tar,A,-1);
        v[now]=merge(v[ls(now)],v[rs(now)]);
        size[now]--;sz[now]--;

        fa[ls(now)]=fa[rs(now)]=now;
        if(max(sz[ls(now)],sz[rs(now)])>sz[now]*alp)
            fre=now;
    }
    void insert(int x,int y)
    {
        fre=0;insert(rt,x,y);
        if(fre)reb(fre);
    }
    void del(int x)
    {
        fre=0;del(rt,x);
        if(fre)reb(fre);
    }
    void find(int now,int pre,int x,int y)
    {
        if(pre+1>=x&&pre+size[now]<=y)
        {
            st[++top]=root[now];
            n1=merge(n1,v[now]);
            return;
        }
        if(pre+size[ls(now)]>=x)
            find(ls(now),pre,x,y);
        if(pre+size[ls(now)]<y)
            find(rs(now),pre+size[ls(now)],x,y);
    }
    node find(int x,int y)
    {
        top=0;n1=node(0,0);
        find(rt,0,x,y);
        return n1;
    }
}tr2;
int main()
{
    scanf("%d%d",&n,&m);n0=n;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    tr2.rt=tr2.build(1,n);
    for(int x,y;m--;)
    {
        scanf("%s",s);
        if(s[0]=='I')
        {
            scanf("%d%d",&x,&y);
            x=(x+ans)%n0+1;y=(y+ans)%(1<<A);n0++;
            tr2.insert(x,y);
        }
        else if(s[0]=='D')
        {
            scanf("%d",&x);
            x=(x+ans)%n0+1;n0--;
            tr2.del(x);
        }
        else if(s[0]=='C')
        {
            scanf("%d%d",&x,&y);
            x=(x+ans)%n0+1;y=(y+ans)%(1<<A);
            tr2.del(x);
            tr2.insert(x,y);
        }
        else
        {
            scanf("%d%d",&x,&y);
            x=(x+ans)%n0+1;y=(y+ans)%n0+1;
            node t=tr2.find(x,y);
            printf("%d\n",ans=tr1.query(t.sx,A));
        }
    }
    return 0;
}
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值