zoukankan      html  css  js  c++  java
  • 【bzoj3681】Arietta 【网络流】【主席树】【启发式合并】

    题目链接
    题解:这是一个网络流的模型,很容易看出来。
    首先让我们考虑一个朴素的网络流建模:
    s>it[i]
    i>j(jd[i]l[i]<=h[j]<=r[i])inf
    >t1
    然后跑最大流就是答案。
    但是n达到了10000,这样子做的时间和空间复杂度显然不能承受。
    bzoj3218一样,我们换一个想法,能否用数据结构来优化建图?
    首先想到的是主席树,但是建边不能支持区间减法。
    orz了题解后,我fa♂现了一个神奇的操作:树上启发式合并!这玩意在GDKOI2018开过讲座。具体做法是这样的:把这棵树轻重链剖分了。对每个点,维护棵下边为子树中所有点的权值的线段树。然后把重儿子的信息通过可持久化与自己的信息合并,子树内其他点暴力插入即可。据资料显示,这样子每个点最多被暴力插进log2n棵权值线段树,每次插入是log2n的时间,点数和边数都大概是(log2n)2级别的。
    怎么开数组呢?一般来说卡到内存上界就好了。233不靠谱
    怎么连边?
    s>it[i]
    i>d[i]线l[i]r[i]
    可持久化时,>inf
    线段树中,>inf
    线>inf
    >t1
    我讲一讲我掉的两个坑,查了我3h:
    1.线段树的叶子节点不能直接向汇点连1的边,而要先连向一个辅助点,再由辅助点连向汇点,因为所有线段树中会出现许多个节点代表原树上的同一个点,要保证原树的每一个点只被选一次。
    2.主席树只要有修改,必须新开一个版本,不能在原来的版本上直接改,不然会修改一些与之前版本的共用节点。(话说bzoj3236我掉了同样的坑啊,不长记性)
    代码:

    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    using namespace std;
    const int N=10005,V=1700005,E=3000005,inf=0x7fffffff;
    int n,m,l,r,d,tt,Cnt,h[N],fa[N],Head[N],To[N],Nxt[N],siz[N],son[N],id[N];
    int cnt=1,tot=1,s=0,t=1,root[N],ch[V][2],head[V],dep[V],to[E],nxt[E],dd[E];
    queue<int> q;
    void Adde(int u,int v){
        To[++Cnt]=v;
        Nxt[Cnt]=Head[u];
        Head[u]=Cnt;
    }
    void adde(int u,int v,int d){
        to[++cnt]=v;
        nxt[cnt]=head[u];
        dd[cnt]=d;
        head[u]=cnt;
        to[++cnt]=u;
        nxt[cnt]=head[v];
        dd[cnt]=0;
        head[v]=cnt;
    }
    void update(int y,int &x,int l,int r,int k,int bl){
        x=++tot;
        if(y){
            adde(x,y,inf);
        }
        ch[x][0]=ch[y][0];
        ch[x][1]=ch[y][1];
        if(l==r){
            if(!id[bl]){
                id[bl]=++tot;
            }
            adde(x,id[bl],inf);
            return;
        }
        int mid=(l+r)/2;
        adde(x,tot+1,inf);
        if(k<=mid){
            update(ch[y][0],ch[x][0],l,mid,k,bl);
        }else{
            update(ch[y][1],ch[x][1],mid+1,r,k,bl);
        }
    }
    void query(int x,int l,int r,int L,int R){
        if(!x){
            return;
        }
        if(L<=l&&R>=r){
            adde(tot,x,inf);
            return;
        }
        int mid=(l+r)/2;
        if(L<=mid){
            query(ch[x][0],l,mid,L,R);
        }
        if(R>mid){
            query(ch[x][1],mid+1,r,L,R);
        }
    }
    void dfs1(int u){
        siz[u]=1;
        int v;
        for(int i=Head[u];i;i=Nxt[i]){
            v=To[i];
            dfs1(v);
            siz[u]+=siz[v];
            if(!son[u]||siz[son[u]]<siz[v]){
                son[u]=v;
            }
        }
    }
    void dfs3(int rt,int u){
        update(root[rt],root[rt],1,n,h[u],u);
        int v;
        for(int i=Head[u];i;i=Nxt[i]){
            v=To[i];
            dfs3(rt,v);
        }
    }
    void dfs2(int u){
        int v;
        for(int i=Head[u];i;i=Nxt[i]){
            v=To[i];
            dfs2(v);
        }
        if(son[u]){
            update(root[son[u]],root[u],1,n,h[u],u);
            for(int i=Head[u];i;i=Nxt[i]){
                v=To[i];
                if(v!=son[u]){
                    dfs3(u,v);
                }
            }
        }else{
            update(0,root[u],1,n,h[u],u);
        }
    }
    bool bfs(){
        memset(dep,0,sizeof(int)*(tot+1));
        dep[s]=1;
        q.push(s);
        while(!q.empty()){
            int u=q.front(),v;
            q.pop();
            for(int i=head[u];i;i=nxt[i]){
                v=to[i];
                if(!dep[v]&&dd[i]){
                    dep[v]=dep[u]+1;
                    q.push(v);
                }
            }
        }
        return dep[t];
    }
    int dfs(int u,int f){
        if(u==t){
            return f;
        }
        int res=0,tmp,v;
        for(int i=head[u];i&&f;i=nxt[i]){
            v=to[i];
            if(dd[i]&&dep[v]==dep[u]+1&&(tmp=dfs(v,min(f,dd[i])))){
                dd[i]-=tmp;
                dd[i^1]+=tmp;
                f-=tmp;
                res+=tmp;
            }
        }
        if(!res){
            dep[u]=0;
        }
        return res;
    }
    int maximumflow(){
        int res=0;
        while(bfs()){
            res+=dfs(s,inf);
        }
        return res;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=2;i<=n;i++){
            scanf("%d",&fa[i]);
            Adde(fa[i],i);
        }
        for(int i=1;i<=n;i++){
            scanf("%d",&h[i]);
        }
        dfs1(1);
        dfs2(1);
        for(int i=1;i<=n;i++){
            adde(id[i],t,1);
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d%d%d",&l,&r,&d,&tt);
            adde(s,++tot,tt);
            query(root[d],1,n,l,r);
        }
        printf("%d
    ",maximumflow());
        return 0;
    }
  • 相关阅读:
    Rxjava2.0 链式请求异常处理
    Android 8.0新特性-取消大部分静态注册广播
    在retrofit+Rxjava中如何取得状态码非200(出现错误)时的response里的body
    关于app更新安装闪退和EditText长按出现的水滴颜色设置问题
    Android实时获取音量(单位:分贝)
    Android开发框架
    Android秒级编译工具Freeline
    Android 解决qq分享后返回程序出现的Bug
    App开发架构指南(谷歌官方文档译文)
    几行代码让状态栏随心所欲
  • 原文地址:https://www.cnblogs.com/2016gdgzoi471/p/9476858.html
Copyright © 2011-2022 走看看