1. 程式人生 > >【BZOJ3681】Arietta 樹鏈剖分+可持久化線段樹優化建圖+網絡流

【BZOJ3681】Arietta 樹鏈剖分+可持久化線段樹優化建圖+網絡流

des 持久化 -s 過程 void 但是 陽光 建圖 ==

【BZOJ3681】Arietta

Description

Arietta 的命運與她的妹妹不同,在她的妹妹已經走進學院的時候,她仍然留在山村中。
但是她從未停止過和戀人 Velding 的書信往來。一天,她準備去探訪他。
對著窗外的陽光,臨行前她再次彈起了琴。
她的琴的發聲十分特殊。
讓我們給一個形式化的定義吧。
所有的 n 個音符形成一棵由音符 C ( 1 號節點) 構成的有根樹,每一個音符有一個音高 Hi 。
Arietta 有 m 個力度,第 i 個力度能彈出 Di 節點的子樹中,音高在 [Li,Ri] 中的任意一個音符。
為了樂曲的和諧,Arietta 最多會彈奏第 i 個力度 Ti 次。

Arietta 想知道她最多能彈出多少個音符。

Input

輸入共 m + 3 行。
第一行兩個整數 n, m ,意義如題目所述。
第二行 n - 1 個整數 Pi ,表示節點 i ( i = 2 . . . n ) 的父親節點的編號。
第三行 n 個整數 Hi 。
接下來的 m 行,每行四個整數 Li,Ri,D,Ti

Output

輸出一個整數表示 Arietta 最多能彈奏多少音符。
數據範圍與約定
對於 100% 的數據,1 ≤ n, m ≤ 10000 。
對於所有數據,1 ≤ Hi , Ti , Pi ≤ n, 1 ≤ Li ≤ Ri ≤ n 。

Sample Input

5 2
1 1 2 2
5 3 2 4 1
1 3 2 1
3 5 1 4

Sample Output

4

HINT

第一個力度彈奏音符5,第二個力度彈奏音符1,2,4。

數據範圍與約定
對於 100% 的數據,1 ≤ n, m ≤ 10000 。
對於所有數據1<=Hi,Ti,Pi<=N,1<=Li<=Ri<=N

題解:樹剖還真是神通廣大啊~

容易想到用最大流解決,但是邊數太大,考慮用線段樹優化建圖的過程。我們對於每個節點都維護一棵線段樹,維護它子樹中所有點的音高。然後我們將x的所有兒子的線段樹與x合並到一起就得到了x的線段樹(然而線段樹合並是不資瓷的。)

暴力合並顯然邊數還是爆炸,但是我們可以對原樹進行輕重鏈剖分,將線段樹變成可持久化線段樹。對於將x的重兒子與x合並這個過程,直接通過可持久化解決,其余的兒子暴力插入,這樣邊數就變成$O(nlog^2_n)$的了。

最後在可持久化線段樹上連邊即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
const int maxn=10010;
int n,m,tot,cnt,S,T,ans;
int fa[maxn],to[maxn*240],next[maxn*240],val[maxn*240],head[maxn*80],d[maxn*80],v[maxn],rt[maxn],siz[maxn],son[maxn];
vector<int> ch[maxn];
struct sag
{
	int ls,rs;
}s[maxn*80];
queue<int> q;
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
inline void add(int a,int b,int c)
{
	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
	to[cnt]=a,val[cnt]=0,next[cnt]=head[b],head[b]=cnt++;
}
void insert(int &x,int y,int l,int r,int a,int b)
{
	x=++tot;
	if(l==r)
	{
		add(x+n,b,1<<30);
		if(y)	add(x+n,y+n,1<<30);
		return ;
	}
	int mid=(l+r)>>1;
	if(a<=mid)	s[x].rs=s[y].rs,insert(s[x].ls,s[y].ls,l,mid,a,b);
	else	s[x].ls=s[y].ls,insert(s[x].rs,s[y].rs,mid+1,r,a,b);
}
void getall(int x,int y)
{
	insert(rt[x],rt[x],1,n,v[y],y);
	for(int i=0;i<(int)ch[y].size();i++)	getall(x,ch[y][i]);
}
void DFS(int x)
{
	siz[x]=1;
	for(int i=0;i<(int)ch[x].size();i++)
	{
		DFS(ch[x][i]),siz[x]+=siz[ch[x][i]];
		if(siz[ch[x][i]]>siz[son[x]])	son[x]=ch[x][i];
	}
	insert(rt[x],rt[son[x]],1,n,v[x],x);
	for(int i=0;i<(int)ch[x].size();i++)	if(ch[x][i]!=son[x])	getall(x,ch[x][i]);
}
void query(int x,int l,int r,int a,int b,int y)
{
	if(!x)	return ;
	if(a<=l&&r<=b)
	{
		add(y+n,x+n,1<<30);
		return ;
	}
	int mid=(l+r)>>1;
	if(a<=mid)	query(s[x].ls,l,mid,a,b,y);
	if(b>mid)	query(s[x].rs,mid+1,r,a,b,y);
}
inline int dfs(int x,int mf)
{
	if(x==T)	return mf;
	int i,k,temp=mf;
	for(i=head[x];i!=-1;i=next[i])	if(d[to[i]]==d[x]+1&&val[i])
	{
		k=dfs(to[i],min(temp,val[i]));
		if(!k)	d[to[i]]=0;
		val[i]-=k,val[i^1]+=k,temp-=k;
		if(!temp)	break;
	}
	return mf-temp;
}
int bfs()
{
	while(!q.empty())	q.pop();
	memset(d,0,sizeof(d));
	int i,u;
	q.push(S),d[S]=1;
	while(!q.empty())
	{
		u=q.front(),q.pop();
		for(i=head[u];i!=-1;i=next[i])	if(!d[to[i]]&&val[i])
		{
			d[to[i]]=d[u]+1;
			if(to[i]==T)	return 1;
			q.push(to[i]);
		}
	}
	return 0;
}
int main()
{
	//freopen("bz3681.out","w",stdout);
	n=rd(),m=rd();
	int i,a,b,x,y;
	memset(head,-1,sizeof(head));
	for(i=2;i<=n;i++)	fa[i]=rd(),ch[fa[i]].push_back(i);
	for(i=1;i<=n;i++)	v[i]=rd();
	DFS(1);
	S=0;
	for(i=1;i<=tot;i++)
	{
		if(s[i].ls)	add(i+n,s[i].ls+n,1<<30);
		if(s[i].rs)	add(i+n,s[i].rs+n,1<<30);
	}
	for(i=1;i<=m;i++)
	{
		a=rd(),b=rd(),x=rd(),y=rd(),add(S,(++tot)+n,y);
		query(rt[x],1,n,a,b,tot);
	}
	T=tot+n+1;
	for(i=1;i<=n;i++)	add(i,T,1);
	while(bfs())	ans+=dfs(S,1<<30);
	printf("%d",ans);
	return 0;
}

【BZOJ3681】Arietta 樹鏈剖分+可持久化線段樹優化建圖+網絡流