【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\n",maximumflow());
    return 0;
}
posted @ 2018-06-15 11:25  ez_2016gdgzoi471  阅读(134)  评论(0编辑  收藏  举报