传送门
思路:
题目来源于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());
}