【bzoj3681】Arietta 【网络流】【主席树】【启发式合并】
题目链接
题解:这是一个网络流的模型,很容易看出来。
首先让我们考虑一个朴素的网络流建模:
然后跑最大流就是答案。
但是n达到了10000,这样子做的时间和空间复杂度显然不能承受。
像bzoj3218一样,我们换一个想法,能否用数据结构来优化建图?
首先想到的是主席树,但是建边不能支持区间减法。
orz了题解后,我fa♂现了一个神奇的操作:树上启发式合并!这玩意在GDKOI2018开过讲座。具体做法是这样的:把这棵树轻重链剖分了。对每个点,维护棵下边为子树中所有点的权值的线段树。然后把重儿子的信息通过可持久化与自己的信息合并,子树内其他点暴力插入即可。据资料显示,这样子每个点最多被暴力插进棵权值线段树,每次插入是的时间,点数和边数都大概是级别的。
怎么开数组呢?一般来说卡到内存上界就好了。233不靠谱
怎么连边?
可持久化时,
线段树中,
我讲一讲我掉的两个坑,查了我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;
}