题面
所有的 n 个音符形成一棵由音符 C ( 1 号节点) 构成的有根树,每一个音符有一个音高 Hi 。
Arietta 有 m 个力度,第 i 个力度能弹出 Di 节点的子树中,音高在 [Li,Ri] 中的任意一个音符。
为了乐曲的和谐,Arietta 最多会弹奏第 i 个力度 Ti 次。
Arietta 想知道她最多能弹出多少个音符。
1 ≤ n, m ≤ 10000 。
链接戳这里!
分析
网络流模型是显然的,重点在于考虑如何优化建图。
一个点希望向一颗树的子树中权值范围在
[
L
,
R
]
[L, R]
[L,R]的点连边。一个容易想到的想法是dfs序在
[
l
,
r
]
[l, r]
[l,r]权值在
[
L
,
R
]
[L, R]
[L,R],并利用二维线段树优化建图。但是二维线段树的空间(以及可能有时间)都是无法承受的。考察第二种思路,将点按照dfs序插入,利用主席树来进行优化建图。然而有一个问题是建图是没有办法进行作差操作的,于是前缀和也没有用武之地。似乎没有办法只把一个点插入一次,尝试能否插入log次。联想到Dsu on Tree的思路,仍然利用主席树,每个节点copy其重子的主席树,并把其他儿子的节点插入进去。这样我们一共有
O
(
n
∗
l
o
g
n
2
)
O(n*log n^2)
O(n∗logn2)条边。
思路
注意事项
- 所有的叶子节点都连向一个单独的音符节点,以防止每个音符被匹配多次
- 每个新建的叶子节点要连向前一个版本的叶子,否则前一个版本相当于白建了。
- 连边的时候不能按照每个节点去dfs,像这样
void ass(int u, int l, int r) {
if(l == r) return ;
if(t[u].ls) adde(u+n, t[u].ls+n, MAXN), ass(t[u].ls, l, mid);
if(t[u].rs) adde(u+n, t[u].rs+n, MAXN), ass(t[u].rs, mid+1, r);
}
for(int i = 1; i <= n; ++i) ass(root[i], 1, MAXN);
我们使用的是主席树,有大量的节点是相同的,这样连边会出现大量重边导致炸空间,相当于主席树白用了。
像下面这样就是对的
for(int i = 1; i <= tcnt; ++i) {
if(t[i].ls) adde(i+n, t[i].ls+n, n);
if(t[i].rs) adde(i+n, t[i].rs+n, n);
}
代码
#include <bits/stdc++.h>
#define il inline
#define ri register int
#define pb push_back
#define fir first
#define sec second
#define MAXN 10050
#define MAXM
#define MOD
#define INF (1<<25)
#define eps (1e-6)
#define mid ((l+r)>>1)
using namespace std;
typedef long long LL;
typedef long double LD;
int n, m, tcnt, ecnt, S, T, root[MAXN], size[MAXN], val[MAXN], q[MAXN*150], level[MAXN*150], vis[MAXN], son[MAXN];
vector <int> adj[MAXN];
struct node {
int v, c; node *next, *rev;
}pool[MAXN*300], *h[MAXN*80], *cur[MAXN*80];
struct Node {
int ls, rs;
}t[MAXN*150];
il void adde(int u, int v, int c) {
node *p = &pool[ecnt++], *q = &pool[ecnt++];
*p = node {v, c, h[u], q}, h[u] = p;
*q = node {u, 0, h[v], p}, h[v] = q;
}
void ins(int &u, int l, int r, int x, int p) {
int tmp = u; u = ++tcnt, t[u] = t[tmp];
if(l == r) {
if(tmp) adde(u+n, tmp+n, MAXN);
adde(u+n, p, MAXN);
return ;
}
if(x <= mid) ins(t[u].ls, l, mid, x, p);
else ins(t[u].rs, mid+1, r, x, p);
}
void link(int u, int l, int r, int tl, int tr, int p) {
if(!u || l > tr || r < tl) return ;
if(tl <= l && r <= tr) return adde(p+n, u+n, MAXN);
link(t[u].ls, l, mid, tl, tr, p), link(t[u].rs, mid+1, r, tl, tr, p);
}
void add(int u, int rt) {
ins(root[rt], 1, MAXN, val[u], u);
for(ri i = 0; i < adj[u].size(); ++i) if(!vis[adj[u][i]]) add(adj[u][i], rt);
}
void dfs(int u) {
size[u] = 1;
for(ri i = 0; i < adj[u].size(); ++i) {
dfs(adj[u][i]), size[u] += size[adj[u][i]];
if(size[adj[u][i]] > size[son[u]]) son[u] = adj[u][i];
}
}
void dsu_on_tree(int u) {
for(ri i = 0; i < adj[u].size(); ++i) dsu_on_tree(adj[u][i]);
root[u] = root[son[u]], vis[son[u]] = 1, add(u, u), vis[son[u]] = 0;
}
bool bfs() {
int front = 0, rear = 1;
for(ri i = S; i <= T; ++i) level[i] = 0;
q[0] = S, level[S] = 1;
while(front < rear) {
int u = q[front++];
for(node *p = h[u]; p; p = p->next) {
if(p->c && !level[p->v]) {
level[p->v] = level[u]+1, q[rear++] = p->v;
if(p->v == T) return 1;
}
}
}
return 0;
}
int dfs(int u, int f) {
if(u == T) return f;
int F, flow = 0;
for(node *&p = cur[u]; p; p = p->next) {
if(p->c && level[p->v] == level[u]+1) {
F = dfs(p->v, min(f-flow, p->c));
p->c -= F, p->rev->c += F, flow += F;
if(flow == f) return flow;
}
}
return flow;
}
int Dinic() {
int ret = 0;
while(bfs()) {
for(ri i = S; i <= T; ++i) cur[i] = h[i];
ret += dfs(S, INF);
}
return ret;
}
int main() {
int p, l, r, d, o;
scanf("%d%d", &n, &m);
for(ri i = 2; i <= n; ++i) scanf("%d", &p), adj[p].pb(i);
for(ri i = 1; i <= n; ++i) scanf("%d", &val[i]);
dfs(1), dsu_on_tree(1);
for(ri i = 1; i <= tcnt; ++i) {
if(t[i].ls) adde(i+n, t[i].ls+n, n);
if(t[i].rs) adde(i+n, t[i].rs+n, n);
}
while(m--) {
scanf("%d%d%d%d", &l, &r, &d, &o);
++tcnt, adde(S, tcnt+n, o);
link(root[d], 1, MAXN, l, r, tcnt);
}
T = ++tcnt+n;
for(ri i = 1; i <= n; ++i) adde(i, T, 1);
printf("%d", Dinic());
return 0;
}