BZOJ 3681: Arietta
数据结构优化建图的网络流,还需要Dsu on tree和可持久化,但也不大难写
很容易想出暴力的做法,把操作和音符看作二分图,然后就是求一个最大匹配,转化一下就是一个最大流
此时的边数目是\(O(nm)\)的,显然需要优化边数
树上子树内信息的维护般就那么几种,这里DFS+主席树上树无法维护(因为颜色不是可减的),那么我们考虑静态科技Dsu on tree
这道题里面我们要给每个点开可持久化线段树,线段树的下标是颜色,这样可以实现某个点子树内的连边操作
那么怎么维护呢,根据Dsu on tree的思想,我们做到一个点时就把它重儿子的线段树继承过来,然后暴力把轻儿子一个一个加进去
由于Dsu on tree的复杂度是\(O(n\log n)\)的(每个点做为轻儿子的次数是\(\log\)级别的),线段树每次新建\(\log n\)个节点,因此总边数是\(O(n\log^2 n)\)的
然后跑一跑网络流就好了,根据Dinic
跑二分图的效率以及网络流跑不满的特性可以卡过此题
PS:这题又卡时间又卡内存,可谓毒瘤至极
#include<cstdio>
#include<iostream>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=10005,INF=1e9;
struct edge
{
int to,nxt,v;
}e[N]; int n,m,cnt,head[N],x,a[N],rt[N],size[N],son[N],l,r,t,tot,lst,cur;
inline void addedge(CI x,CI y)
{
e[++cnt]=(edge){y,head[x],0}; head[x]=cnt;
}
#define to e[i].to
namespace NF //Network Flow
{
const int N=1300005,M=2500005;
edge e[M]; int cnt=1,head[N],cur[N],q[N],dep[N],s,t;
inline void addedge(CI x,CI y,CI z)
{
e[++cnt]=(edge){y,head[x],z}; head[x]=cnt;
e[++cnt]=(edge){x,head[y],0}; head[y]=cnt;
}
inline bool BFS(void)
{
RI H=0,T=1; memset(dep,0,t+1<<2); q[dep[s]=1]=s;
while (H<T)
{
int now=q[++H]; for (RI i=head[now];i;i=e[i].nxt)
if (e[i].v&&!dep[to]) dep[to]=dep[now]+1,q[++T]=to;
}
return dep[t];
}
inline int DFS(CI now,CI tar,int dis)
{
if (now==tar) return dis; int ret=0;
for (RI& i=cur[now];i&&dis;i=e[i].nxt)
if (e[i].v&&dep[to]==dep[now]+1)
{
int dist=DFS(to,tar,min(dis,e[i].v));
dis-=dist; ret+=dist; e[i].v-=dist; e[i^1].v+=dist;
}
if (!ret) dep[now]=0; return ret;
}
inline int Dinic(int ret=0)
{
while (BFS()) memcpy(cur,head,t+1<<2),ret+=DFS(s,t,INF); return ret;
}
};
class Segment_Tree
{
private:
static const int N=1300005;
struct segment
{
int ch[2];
}node[N];
public:
#define lc(x) node[x].ch[0]
#define rc(x) node[x].ch[1]
#define TN CI l=1,CI r=n
inline void insert(CI lst,int& now,CI pos,CI id,TN)
{
node[now=++tot]=node[lst]; if (lst) NF::addedge(now,lst,INF);
if (l==r) return NF::addedge(now,id,INF); int mid=l+r>>1;
if (pos<=mid) insert(lc(lst),lc(now),pos,id,l,mid);
else insert(rc(lst),rc(now),pos,id,mid+1,r);
}
inline void link(CI now,CI beg,CI end,CI id,TN)
{
if (!now) return; if (beg<=l&&r<=end) return NF::addedge(id,now,INF); int mid=l+r>>1;
if (beg<=mid) link(lc(now),beg,end,id,l,mid); if (end>mid) link(rc(now),beg,end,id,mid+1,r);
}
inline void relink(void)
{
for (RI i=n+1;i<=tot;++i)
{
if (lc(i)) NF::addedge(i,lc(i),INF);
if (rc(i)) NF::addedge(i,rc(i),INF);
}
}
#undef lc
#undef rc
#undef TN
}SEG;
inline void expand(CI now)
{
SEG.insert(lst,cur,a[now],now); lst=cur;
for (RI i=head[now];i;i=e[i].nxt) expand(to);
}
inline void DFS1(CI now=1)
{
size[now]=1; int mx=0; for (RI i=head[now];i;i=e[i].nxt)
DFS1(to),size[now]+=size[to],size[to]>mx&&(mx=size[to],son[now]=to);
}
inline void DFS2(CI now=1)
{
RI i; for (i=head[now];i;i=e[i].nxt) DFS2(to);
lst=rt[son[now]]; SEG.insert(lst,cur,a[now],now); lst=cur;
for (i=head[now];i;i=e[i].nxt) if (to!=son[now]) expand(to); rt[now]=lst;
}
#undef to
int main()
{
RI i; for (scanf("%d%d",&n,&m),i=2;i<=n;++i) scanf("%d",&x),addedge(x,i);
for (tot=n,i=1;i<=n;++i) scanf("%d",&a[i]); DFS1(); DFS2(); SEG.relink();
for (NF::s=tot+m+1,NF::t=tot+m+2,i=1;i<=n;++i) NF::addedge(i,NF::t,1);
for (i=1;i<=m;++i) scanf("%d%d%d%d",&l,&r,&x,&t),
NF::addedge(NF::s,tot+i,t),SEG.link(rt[x],l,r,tot+i);
return printf("%d",NF::Dinic()),0;
}
辣鸡老年选手AFO在即