Description
装饰者坐在树荫下听着长者讲述以前的故事:
大神 yk 非常喜欢树,便钦点班里的 n 个小蒟蒻站在一棵 n 个点以 1 为根的树上,并且每个点上恰好有 1 个小蒟蒻。
大神 yk 非常喜欢 fake,尤其是 fake 比他弱的人。根据可靠消息,大神 yk 拟定了m 个假人计划,每个假人计划形如 fake 树上从点 u 到点 v 的简单路径上站的小蒟蒻。但大神 yk 不喜欢拐角,所以假人计划选择的简单路径的端点满足 v 在 1 到 u 的简单路径上或者 u 在 1 到 v 的简单路径上。
每个小蒟蒻毕竟是人,忍耐是有限度的,站在 i 号点的小蒟蒻的忍耐值为 ci。当这个小蒟蒻被 fake 的次数超过 ci 后,这个小蒟蒻会非常地愤怒。
大神 yk 可以从 m 个假人计划中选出任意多个执行,但是大神 yk 不想让任意一个小蒟蒻感到愤怒,因为这样会破坏友谊。
装饰者听到这里,很好奇大神 yk 最多能实施多少假人计划。但是这个问题太简单了,装饰者秒掉了它。于是它被当成模拟赛的签到题扔你做。
Input
第一行有 2 个正整数 n, m。
第二行有 n 个非负整数 c1, · · · , cn,描述小蒟蒻的忍耐值。
接下来有 n n 1 行,每行两个正整数 u, v,描述树上的一条边。
最后有 m 行,每行 2 个正整数 u, v 表示每个假人计划所选择的简单路径。
Output
仅一行,1 个整数,表示大神 yk 最多能实施的假人计划数。
Sample Input
5 3
1 1 2 1 1
1 2
1 3
3 4
3 5
1 4
1 2
3 5
Sample Output
2
Data Constraint
赛时
这题比赛的时候比较逗比。
无脑直接想了个sb贪心。
由于被T3T1搞崩心态了,这题以为又是一道省选+的题。
然后就上了LCT
打完我才发现我的贪心有点bug。
然后转战可撤销堆。
等我大致想到后,比赛已经结束了。
我才不会告诉你可撤销堆依然是错的,但是应该可以水很多分,然鹅他是捆绑数据。
题解
也是贪心。
直接在树上跑,每个点用线段树维护其上面节点的deep。
然后每次如果小蒟蒻不能被fake后,就贪心把deep最小的几个删掉。
然后线段树合并即可。
标程
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <cctype>
using namespace std;
const int maxn=1000010;
int n,m,a[maxn],fa[maxn],val[maxn],de[maxn],dep[maxn],root[maxn],x,y,zd,ans,ii[maxn];
int tot,nex[maxn*2],las[maxn*2],tov[maxn*2];
int tot1,nex1[maxn*2],las1[maxn*2],tov1[maxn*2];
int sum[10*maxn],ls[10*maxn],rs[10*maxn],lazy[10*maxn],cnt;
int insert(int x,int y)
{
tot++;
tov[tot]=y;
nex[tot]=las[x];
las[x]=tot;
}
int insert1(int x,int y)
{
tot1++;
tov1[tot1]=y;
nex1[tot1]=las1[x];
las1[x]=tot1;
}
void downlazy(int x)
{
if (lazy[x]==1)
{
sum[ls[x]]=0;
sum[rs[x]]=0;
lazy[ls[x]]=1;
lazy[rs[x]]=1;
lazy[x]=0;
}
}
int merge(int x,int y)
{
downlazy(x);
downlazy(y);
if (x==0) return y;
if (y==0) return x;
int t=++cnt;
sum[t]=sum[x]+sum[y];
ls[t]=merge(ls[x],ls[y]);
rs[t]=merge(rs[x],rs[y]);
return t;
}
void modify(int x,int l,int r,int wz,int g)
{
if (l==r)
{
sum[x]+=g;
}
else
{
downlazy(x);
int mid=(l+r)/2;
if (mid>=wz)
{
if (ls[x]==0) ls[x]=++cnt;
modify(ls[x],l,mid,wz,g);
}
else
{
if (rs[x]==0) rs[x]=++cnt;
modify(rs[x],mid+1,r,wz,g);
}
sum[x]=sum[ls[x]]+sum[rs[x]];
}
}
void query(int x,int l,int r,int wz)
{
if (l==r)
{
ans+=sum[x];
sum[x]=0;
}
else
{
downlazy(x);
int mid=(l+r)/2;
if (mid>=wz)
{
query(ls[x],l,mid,wz);
}
else
{
query(rs[x],mid+1,r,wz);
}
sum[x]=sum[ls[x]]+sum[rs[x]];
}
}
void clear(int x,int l,int r,int siz)
{
if (l==r)
{
sum[x]-=siz;
}
else
{
downlazy(x);
int mid=(l+r)/2;
if (sum[ls[x]]<=siz)
{
siz-=sum[ls[x]];
sum[ls[x]]=0;
lazy[ls[x]]=1;
clear(rs[x],mid+1,r,siz);
}
else clear(ls[x],l,mid,siz);
sum[x]=sum[ls[x]]+sum[rs[x]];
}
}
void dfs(int x)
{
zd=max(zd,dep[x]);
for (ii[x]=las[x];ii[x]>0;ii[x]=nex[ii[x]])
{
if (tov[ii[x]]!=fa[x])
{
fa[tov[ii[x]]]=x;
dep[tov[ii[x]]]=dep[x]+1;
dfs(tov[ii[x]]);
}
}
}
void getans(int x)
{
root[x]=0;
for (ii[x]=las[x];ii[x]>0;ii[x]=nex[ii[x]])
{
if (tov[ii[x]]!=fa[x])
{
getans(tov[ii[x]]);
root[x]=merge(root[x],root[tov[ii[x]]]);
}
}
if (root[x]==0)
{
cnt++;
root[x]=cnt;
}
for (ii[x]=las1[x];ii[x]>0;ii[x]=nex1[ii[x]])
modify(root[x],1,zd,tov1[ii[x]],1);
int total=sum[root[x]]-a[x];
if (total>0)
{
clear(root[x],1,zd,total);
}
if (de[x]>0)
query(root[x],1,zd,dep[x]);
}
int main()
{
// freopen("data.in","r",stdin);
freopen("fake.in","r",stdin);
freopen("fake.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for (int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
insert(x,y);
insert(y,x);
}
dep[1]=1;
dfs(1);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
if (x==y)
{
int j=0;
}
if (dep[x]>dep[y])
{
swap(x,y);
}
de[x]++;
val[y]++;
insert1(y,dep[x]);
}
getans(1);
printf("%d
",ans);
}