想了很久才会。
这里实际上给出了一棵带权有根树,我们需要给每个点分配一个重儿子,使每个叶子得到一条链。令第
i
i
i条河流对应点到根深度为
d
i
d_i
di,显然它的答案为
n
−
f
(
d
i
)
+
1
n-f(d_i)+1
n−f(di)+1,其中
f
(
d
i
)
f(d_i)
f(di)为最多能分配出的长度
≤
d
i
\leq d_i
≤di的链数目。
令
S
(
i
)
S(i)
S(i)为
f
(
i
)
f(i)
f(i)对应的某个方案中所选的
≤
i
\leq i
≤i的链的叶子集合。我们断言,对于
∀
j
≥
i
\forall j\geq i
∀j≥i,一定存在某个方案使
S
(
i
)
⊇
S
(
j
)
S(i)\supseteq S(j)
S(i)⊇S(j)。也即,我们可以逐渐在
S
S
S中加入元素来得到答案。证明可以考虑调整法,比较简单。
考虑给定一个集合
S
S
S与允许的长度
w
w
w,如何判定是否有合法方案。对于每个非根节点
x
x
x,若
x
x
x子树中有不在
S
S
S中的点
y
y
y,我们会让
y
y
y的路径一直向上连到
x
x
x与父亲的连边,否则我们会选择子树中深度最浅的点
y
y
y一直往上连,令
y
y
y到
x
x
x父亲的路径长度为
m
x
m_x
mx,那么必须满足
w
≥
m
x
w\geq m_x
w≥mx。容易发现这是充要条件,满足这些条件的话显然能构造出合法方案。
现在我们得到了一个贪心算法:从小到大扫描
d
i
d_i
di,我们维护
S
S
S,尝试在
S
S
S中加入新的元素
x
x
x,使得仍然合法。若加入
x
x
x后合法,这意味着
x
x
x到根的路径上所有点
u
u
u均满足
S
S
S加入
x
x
x后在
u
u
u子树中仍未包含所有点,或
d
i
≥
m
u
d_i\geq m_u
di≥mu。维护当前合法的点
u
u
u的集合
T
T
T,用一个DFS序线段树支持寻找一个到根路径上的点均在
T
T
T中的叶子即可。每次加入
x
x
x后还要更新
T
T
T(有可能某个
T
T
T中的点子树中不在
S
S
S中的点数
=
1
=1
=1,要从
T
T
T中删去),不过修改的一定是从
x
x
x向上的若干级祖先,暴力跳即可(需要支持查询一个点子树中
S
S
S中点数)。
时间复杂度为
O
(
(
n
+
m
)
log
n
)
\mathcal O((n+m)\log n)
O((n+m)logn)。
#include <bits/stdc++.h>
#define FR first
#define SE second
#define inf 0x3f3f3f3f3f3f3f3fLL
#define lowbit(x) (x&-x)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pr;
namespace BIT {
int sumv[500005];
void add(int x,int n,int num) {
for(;x<=n;x+=lowbit(x)) sumv[x]+=num;
}
int sum(int x) {
int s=0;
for(;x;x-=lowbit(x)) s+=sumv[x];
return s;
}
int sum(int l,int r) {
return sum(r)-sum(l-1);
}
}
namespace SGT {
pr minn[2000000];
int addv[2000000];
inline void pushdown(int o) {
if (addv[o]) {
addv[o*2]+=addv[o];
addv[o*2+1]+=addv[o];
minn[o*2].FR+=addv[o];
minn[o*2+1].FR+=addv[o];
addv[o]=0;
}
}
inline void pushup(int o) {
minn[o]=min(minn[o*2],minn[o*2+1]);
}
void build(int l,int r,int o) {
if (l==r) minn[o]=pr(1,l);
else {
int m=((l+r)>>1);
build(l,m,o*2);
build(m+1,r,o*2+1);
pushup(o);
}
}
void update1(int l,int r,int o,int p) {
if (l==r) minn[o]=pr(inf,l);
else {
pushdown(o);
int m=((l+r)>>1);
if (m>=p) update1(l,m,o*2,p);
else update1(m+1,r,o*2+1,p);
pushup(o);
}
}
void update2(int l,int r,int o,int lx,int rx,int p) {
if (l>=lx&&r<=rx) {
addv[o]+=p;
minn[o].FR+=p;
}
else {
pushdown(o);
int m=((l+r)>>1);
if (m>=lx) update2(l,m,o*2,lx,rx,p);
if (m<rx) update2(m+1,r,o*2+1,lx,rx,p);
pushup(o);
}
}
int query() {
return (minn[1].FR<=0)?minn[1].SE:0;
}
}
vector <int> e[1000005];
char str[500005][15];
int num[1000005],p[1000005];
int n,m;
ll dis[1000005],minn[1000005];
int col[1000005];
int dfn[1000005],ed[1000005],id[500005],dfs_cnt;
void dfs(int x) {
dfn[x]=dfs_cnt+1;
if (x>m) {
ed[x]=++dfs_cnt;
id[dfs_cnt]=x;
minn[x]=num[x];
return;
}
minn[x]=inf;
col[x]=1;
for(int i=0;i<e[x].size();i++) {
int u=e[x][i];
dis[u]=dis[x]+num[u];
dfs(u);
minn[x]=min(minn[x],minn[u]);
}
minn[x]+=num[x];
ed[x]=dfs_cnt;
}
void modify(int x) {
BIT::add(dfn[x],n,-1);
while (p[x]) {
x=p[x];
if (BIT::sum(dfn[x],ed[x])>1) return;
if (col[x]==1) {
col[x]=0;
SGT::update2(1,n,1,dfn[x],ed[x],1);
}
}
}
pr val[500005];
int ans[500005],q[1000005];
bool cmp(int x,int y) {
return minn[x]<minn[y];
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) {
scanf("%s%d%d",str[i],&p[m+i],&num[m+i]);
e[p[m+i]].push_back(m+i);
}
for(int i=1;i<=m;i++) {
int x;
scanf("%d%d",&p[i],&num[i]);
e[p[i]].push_back(i);
}
dfs(0);
for(int i=1;i<=n;i++) val[i]=pr(dis[m+i],i);
sort(val+1,val+n+1);
for(int i=1;i<=n+m;i++) q[i]=i;
sort(q+1,q+n+m+1,cmp);
int r=1,s=n;
SGT::build(1,n,1);
for(int i=1;i<=n;i++) BIT::add(i,n,1);
for(int i=1;i<=n;i++) {
ll v=val[i].FR;
while (r<=n+m&&minn[q[r]]<=v) {
int x=q[r++];
if (col[x]==0) SGT::update2(1,n,1,dfn[x],ed[x],-1);
col[x]=2;
}
while (SGT::query()) {
int x=id[SGT::query()];
s--;
modify(x);
SGT::update1(1,n,1,dfn[x]);
}
ans[val[i].SE]=s+1;
}
for(int i=1;i<=n;i++) printf("%s %d\n",str[i],ans[i]);
return 0;
}