BZOJ3681: Arietta

题解:

数据结构来优化网络流,貌似都是用一段区间来表示一个点,然后各种乱搞。。。

发现主席树好吊。。。在树上建主席树貌似有三种方法:

1.建每个点到根节点这条链上的主席树,可以回答和两点间的路径的XX问题。

2.按DFS序然后就成了序列上的主席树,可以回答子树第k大等XX问题。

3.先给每个点单独建立主席树,然后dfs下去,并且将一个点的所有子树合并起来,相当于新建了n棵主席树,但这样可以解决该题。。。

神犇勿喷。。。

因为这样从rt[x]查询到的区间就可以直接表示满足题意的点了,然后就是最大流了。

代码:

 #include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<string>
#define inf 1000000000
#define maxn 100000+5
#define maxm 1000000+5
#define eps 1e-10
#define ll long long
#define pa pair<int,int>
#define for0(i,n) for(int i=0;i<=(n);i++)
#define for1(i,n) for(int i=1;i<=(n);i++)
#define for2(i,x,y) for(int i=(x);i<=(y);i++)
#define for3(i,x,y) for(int i=(x);i>=(y);i--)
#define for4(i,x) for(int i=head[x],y=e[i].go;i;i=e[i].next,y=e[i].go)
#define mod 1000000007
#define mid ((l+r)>>1)
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=*x+ch-'';ch=getchar();}
return x*f;
}
int n,m,s,t,cnt,maxflow,tot=,head[maxm],cur[maxm],h[maxm];
queue<int>q;
int rt[maxn],lc[maxm],rc[maxm];
struct edge{int go,next,v;}e[maxm];
vector<int> son[maxn];
inline void add(int x,int y,int v)
{
e[++tot]=(edge){y,head[x],v};head[x]=tot;
e[++tot]=(edge){x,head[y],};head[y]=tot;
}
bool bfs()
{
for(int i=;i<=cnt;i++)h[i]=-;
q.push(s);h[s]=;
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=head[x];i;i=e[i].next)
if(e[i].v&&h[e[i].go]==-)
{
h[e[i].go]=h[x]+;q.push(e[i].go);
}
}
return h[t]!=-;
}
int dfs(int x,int f)
{
if(x==t) return f;
int tmp,used=;
for(int i=cur[x];i;i=e[i].next)
if(e[i].v&&h[e[i].go]==h[x]+)
{
tmp=dfs(e[i].go,min(e[i].v,f-used));
e[i].v-=tmp;if(e[i].v)cur[x]=i;
e[i^].v+=tmp;used+=tmp;
if(used==f)return f;
}
if(!used) h[x]=-;
return used;
}
void dinic()
{
maxflow=;
while(bfs())
{
for (int i=;i<=cnt;i++)cur[i]=head[i];maxflow+=dfs(s,inf);
}
}
inline void insert(int x)
{
int l,r;
for(l=,r=n,++cnt;l<r;++cnt)
if(x<=mid)
{
add(cnt,lc[cnt]=cnt+,inf);
r=mid;
}else
{
add(cnt,rc[cnt]=cnt+,inf);
l=mid+;
}
add(cnt,t,);
}
inline int merge(int x,int y,int l,int r)
{
if(x*y==)return x+y;
int k=++cnt;
if(l==r)add(k,x,inf),add(k,y,inf);
else
{
add(k,lc[k]=merge(lc[x],lc[y],l,mid),inf);
add(k,rc[k]=merge(rc[x],rc[y],mid+,r),inf);
}
return k;
}
inline void dfs(int x)
{
if(!son[x].size())return;
for(int i=son[x].size()-;~i;i--)
{
dfs(son[x][i]);
rt[x]=merge(rt[x],rt[son[x][i]],,n);
}
}
inline void query(int k,int l,int r,int x,int y)
{
if(!k)return;
if(l==x&&r==y){add(cnt,k,inf);return;}
if(y<=mid)query(lc[k],l,mid,x,y);
else if(x>mid)query(rc[k],mid+,r,x,y);
else query(lc[k],l,mid,x,mid),query(rc[k],mid+,r,mid+,y);
}
int main()
{
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
n=read();m=read();s=;t=;cnt=t;
for2(i,,n)son[read()].push_back(i);
for1(i,n)rt[i]=cnt+,insert(read());
dfs();
while(m--)
{
int l=read(),r=read(),x=read(),y=read();
add(s,++cnt,y);
query(rt[x],,n,l,r);
}
dinic();
cout<<maxflow<<endl;
return ;
}

3681: Arietta

Time Limit: 20 Sec  Memory Limit: 64 MB
Submit: 64  Solved: 28
[Submit][Status]

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

Source

上一篇:Maven 项目 查找指定包的引用位置


下一篇:单元测试系列之三:JUnit单元测试规范