bzoj3681 Arietta(dsu on tree+主席树优化建图+网络流)

显然就是个建图最大流。然而边数爆炸。
我们考虑每个节点维护一棵线段树表示子树信息来优化建图,然后边数还是爆炸。
我们再考虑树上建主席树来优化建图,不错,好像可以了,然而子树信息不可减(维护的是权值为x的点的标号)gg

这时候我们要用到一种黑科技:dsu on tree!
也就是树上启发式合并。更多参考资料:portal

大概就是划分轻重链,然后轻边暴力,这样复杂度就科学了!
具体说下:每个点的线段树由它的重儿子可持久化而来,然后暴力插入所有轻儿子的子树。因为每个点往上跳最多logn条重链,所以一个点最多被暴力插入logn次,因此空间复杂度和时间复杂度都是 O(nlog2n) 的。

然后我们就成功优化建图了!点数 O(nlog2n+m+n) ,边数 O(2nlog2n+mlogn)

注意一个点的线段树内同一权值可能有多个标号,因此可持久化时要注意特殊处理叶子。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 10010
#define M 1300010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,m,a[N],rt[N],owo=0,sz[N],son[N],h[M],num=1,lev[M],cur[M],T,ans=0;
vector<int>Son[N];
struct node{
    int lc,rc;
}tr[M];
struct edge{
    int to,next,val;
}data[M<<1];
inline void add(int x,int y,int val){
    data[++num].to=y;data[num].next=h[x];h[x]=num;data[num].val=val;
    data[++num].to=x;data[num].next=h[y];h[y]=num;data[num].val=0;
}
inline void ins(int &p,int l,int r,int x,int id){
    if(l==r){++owo;add(owo+n,id,inf);if(p) add(owo+n,p+n,inf);p=owo;return;}
    tr[++owo]=tr[p];p=owo;int mid=l+r>>1;
    if(x<=mid) ins(tr[p].lc,l,mid,x,id);
    else ins(tr[p].rc,mid+1,r,x,id);
}
void dfs1(int x,int id){
    ins(rt[id],1,n,a[x],x);
    for(int i=0;i<Son[x].size();++i) dfs1(Son[x][i],id);
}
void dfs(int x){
    sz[x]=1;son[x]=0;
    for(int i=0;i<Son[x].size();++i){
        int y=Son[x][i];dfs(y);if(sz[y]>sz[son[x]]) son[x]=y;sz[x]+=sz[y];
    }rt[x]=rt[son[x]];ins(rt[x],1,n,a[x],x);
    for(int i=0;i<Son[x].size();++i){
        int y=Son[x][i];if(y!=son[x]) dfs1(y,x);
    }
}
inline void cover(int p,int l,int r,int x,int y){
    if(!p) return;
    if(x<=l&&r<=y){add(owo,p+n,inf);return;}
    int mid=l+r>>1;
    if(x<=mid) cover(tr[p].lc,l,mid,x,y);
    if(y>mid) cover(tr[p].rc,mid+1,r,x,y);
}
inline bool bfs(){
    queue<int>q;for(int i=0;i<=T;++i) lev[i]=0;
    q.push(0);lev[0]=1;
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int i=h[x];i;i=data[i].next){
            int y=data[i].to;if(lev[y]||!data[i].val) continue;
            lev[y]=lev[x]+1;if(y==T) return 1;q.push(y);
        }
    }return 0;
}
inline int dinic(int x,int low){
    if(x==T) return low;int tmp=low;
    for(int &i=cur[x];i;i=data[i].next){
        int y=data[i].to;if(lev[y]!=lev[x]+1||!data[i].val) continue;
        int res=dinic(y,min(tmp,data[i].val));
        if(!res) lev[y]=0;else tmp-=res,data[i].val-=res,data[i^1].val+=res;
        if(!tmp) return low;
    }return low-tmp;
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();m=read();
    for(int i=2;i<=n;++i) Son[read()].push_back(i);
    for(int i=1;i<=n;++i) a[i]=read();dfs(1);
    for(int i=1;i<=owo;++i){
        if(tr[i].lc) add(i+n,tr[i].lc+n,inf);
        if(tr[i].rc) add(i+n,tr[i].rc+n,inf);
    }owo+=n;
    while(m--){
        int l=read(),r=read(),x=read(),val=read();
        add(0,++owo,val);cover(rt[x],1,n,l,r);
    }T=++owo;for(int i=1;i<=n;++i) add(i,T,1);
    while(bfs()){memcpy(cur,h,sizeof(h));ans+=dinic(0,inf);}
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值