【BZOJ3681】Arietta,主席树优化网络流

传送门
思路:
题目来源于Contest Hunter
红太阳mrazer和我说的
第一次做这种数据结构优化网络流的题目之前只是停留在口胡的地步
网上的题解不多,官方题解给出的比较标准的做法是线段树合并+可持久化
不过,蒟蒻表示并没有看懂这种做法,而且好像后来有人发现如果点权相同,64MB的内存根本开不下?
回到题目,建图是显而易见的,关键在如何优化 O(nm) 的边数
暴力的想法是每个节点开一颗权值线段树维护子树内的信息,往区间代表点连边,叶子节点往每个树节点的代表点连边,空间复杂度和时间复杂度都不科学
借助Yveh博客中dsu on tree的思想,我们可以把节点x重儿子的线段树作为节点x的线段树,然后暴力轻儿子所在子树中的信息,这样做可知每个节点的信息最多被暴力统计 logn 次(也就是其到根路径上重链的数量),空间复杂度 O(nlog2n)
每插入一个点,新建的点要向前一个对应的区间代表点连边,流量inf
说白了就是可持久化权值线段树+启发式合并,应该也是题解中给出的一种做法
连边的时候找对应的权值区间,连边即可,叶子节点也要向外连边
感觉程序有bug,如果有人能hack掉请在下面评论,谢谢
代码:

#include<cstdio>
#include<vector>
#include<iostream>
#include<queue>
#define inf 1e9
#define ls(x) tr[x].ch[0]
#define rs(x) tr[x].ch[1] 
#define M 10005
using namespace std;
int in()
{
    int t=0;char ch=getchar();
    while (ch>'9'||ch<'0') ch=getchar();
    while (ch>='0'&&ch<='9') t=(t<<1)+(t<<3)+ch-48,ch=getchar();
    return t;
}
int n,m,cnt;
int h[M],siz[M],son[M],root[M];
queue<int>q;
struct node{
    int l,r,d,t;
}Q[M];
struct tree{
    int ch[2];
}tr[130*M];
vector<int>e[M];
namespace network
{
    int s,t,tot=1,first[M*130],cur[M*130],dis[M*130];
    struct edge{
        int v,w,next;
    }e[M*300];
    void add(int x,int y,int z)
    {
        e[++tot].v=y;e[tot].w=z;e[tot].next=first[x];first[x]=tot;
        e[++tot].v=x;e[tot].w=0;e[tot].next=first[y];first[y]=tot;
    } 
    bool bfs()
    {
        for (int i=0;i<=t;++i) dis[i]=0,cur[i]=first[i];
        int x;
        dis[s]=1;
        for (q.push(s);!q.empty();q.pop())
        {
            x=q.front();
            for (int i=first[x];i;i=e[i].next)
            if (!dis[e[i].v]&&e[i].w)
                dis[e[i].v]=dis[x]+1,
                q.push(e[i].v);
        }
        return dis[t]>0;
    }
    int dfs(int x,int maxn)
    {
        if (x==t) return maxn;
        int used=0,k;
        for (int i=cur[x];i;i=e[i].next)
        if (dis[e[i].v]==dis[x]+1)
        {
            k=dfs(e[i].v,min(maxn-used,e[i].w));
            e[i].w-=k;e[i^1].w+=k;
            if (e[i].w) cur[x]=i;
            used+=k;
            if (used==maxn) return maxn;
        }
        if (!used) dis[x]=0;
        return used;
    }
    int dinic()
    {
        int ans=0;
        while (bfs())
        ans+=dfs(s,inf);
        return ans;
    }
}
void build(int rt,int now,int L,int R,int p)
{
    if (rt) network::add(now,rt,inf);
    if (L==R) return void(network::add(now,p,1));
    int mid=(L+R)>>1;
    if (mid<h[p])
        ls(now)=ls(rt),
        rs(now)=++cnt,
        network::add(now,rs(now),inf),
        build(rs(rt),rs(now),mid+1,R,p);
    else
        rs(now)=rs(rt),
        ls(now)=++cnt,
        network::add(now,ls(now),inf),
        build(ls(rt),ls(now),L,mid,p);
}
void dfs(int x)
{
    siz[x]=1;
    for (int v,i=0;i<e[x].size();++i)
    {
        v=e[x][i];
        dfs(v);
        siz[x]+=siz[v];
        if (siz[son[x]]<siz[v]) son[x]=v;
    }
    root[x]=root[son[x]];
    int tmp=root[x];
    build(tmp,root[x]=++cnt,1,n,x);
    for (int v,i=0;i<e[x].size();++i)
    {
        v=e[x][i];
        if (v==son[x]) continue;
        for (q.push(v);!q.empty();q.pop())
        {
            v=q.front();
            tmp=root[x];
            build(tmp,root[x]=++cnt,1,n,v);
            for (int j=0;j<e[v].size();++j) q.push(e[v][j]);
        }
    }
}
void get(int rt,int L,int R,int id)
{
    if (Q[id].l<=L&&R<=Q[id].r) return void(network::add(id+n,rt,inf));
    int mid=L+R>>1;
    if (mid>=Q[id].l) get(ls(rt),L,mid,id);
    if (mid<Q[id].r) get(rs(rt),mid+1,R,id);
}
main()
{
    n=in();m=in();
    cnt=n+m;
    for (int i=2;i<=n;++i) e[in()].push_back(i);
    for (int i=1;i<=n;++i) h[i]=in();
    for (int i=1;i<=m;++i) Q[i]=(node){in(),in(),in(),in()},network::add(network::s,i+n,Q[i].t);
    dfs(1);
    network::s=0;network::t=cnt+1;
    for (int i=1;i<=m;++i)  get(root[Q[i].d],1,n,i);
    for (int i=1;i<=n;++i) network::add(i,network::t,1);
    printf("%d\n",network::dinic());
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值