jzoj6370. 【NOIP2019模拟2019.9.28】基础 fake 练习题

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\n",ans);
}
posted @ 2019-09-29 22:38  RainbowCrown  阅读(327)  评论(0编辑  收藏  举报