【XSY2667】摧毁图状树 贪心 堆 DFS序 线段树

题目大意

  给你一棵有根树,有\(n\)个点。还有一个参数\(k\)。你每次要删除一条长度为\(k\)\(k\)个点)的祖先-后代链,问你最少几次删完。现在有\(q\)个询问,每次给你一个\(k\),问你答案是多少。

  \(n\leq {10}^5,k\leq {10}^9\)

题解

  设\(l\)为这棵树的叶子个数,显然当\(k>\)树的深度时答案都是\(l\)

  下面要证明:答案是\(O(l+\frac{n-l}{k})\)的。

  我们从下往上贪心,每次选择一个未被覆盖的深度最深的点,覆盖这个点网上的一条链。我们把这些选择的点称为关键点。把所有关键点到父亲的连边断开。

  包含根的那个连通块和叶子节点的贡献是\(O(l)\)的。

  对于其他连通块,显然深度最深的点到关键点的距离是\(k-1\),所以连通块的大小不小于\(k\)。又因为每个连通块只有一个关键点,所以这部分的贡献是\(O(\frac{n-l}{k})\)的。

  对于每个\(k\),从叶子节点的上一层关键点开始暴力做就行了。查询一个点是否被覆盖就用DFS序+线段树(查询这个点的子树深度最浅的点深度是多少)。

  时间复杂度:\(O(n\log^2 n)\)(调和级数求和再加上数据结构的\(\log\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
#include<queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void sort(int &a,int &b)
{
	if(a>b)
		swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
	char str[100];
	sprintf(str,"%s.in",s);
	freopen(str,"r",stdin);
	sprintf(str,"%s.out",s);
	freopen(str,"w",stdout);
#endif
}
int rd()
{
	int s=0,c;
	while((c=getchar())<'0'||c>'9');
	do
	{
		s=s*10+c-'0';
	}
	while((c=getchar())>='0'&&c<='9');
	return s;
}
void put(int x)
{
	if(!x)
	{
		putchar('0');
		return;
	}
	static int c[20];
	int t=0;
	while(x)
	{
		c[++t]=x%10;
		x/=10;
	}
	while(t)
		putchar(c[t--]+'0');
}
int upmin(int &a,int b)
{
	if(b<a)
	{
		a=b;
		return 1;
	}
	return 0;
}
int upmax(int &a,int b)
{
	if(b>a)
	{
		a=b;
		return 1;
	}
	return 0;
}
vector<int> g[100010];
int f[100010][20];
int st[100010];
int ed[100010];
int d[100010];
int ti;
int maxd;
int leaves;
int s[100010];
vector<int> c[100010];
void dfs(int x,int fa,int dep)
{
	maxd=max(maxd,dep);
	f[x][0]=fa;
	d[x]=dep;
	st[x]=++ti;
	int i;
	for(i=1;i<=17;i++)
		f[x][i]=f[f[x][i-1]][i-1];
	int num=0;
	s[x]=0x7fffffff;
	for(auto v:g[x])
		if(v!=fa)
		{
			dfs(v,x,dep+1);
			s[x]=min(s[x],s[v]+1);
			num++;
		}
	if(!num)
	{
		s[x]=0;
		leaves++;
	}
	ed[x]=ti;
}
int jump(int x,int d)
{
	int i;
	for(i=17;i>=0;i--)
		if(d&(1<<i))
			x=f[x][i];
	return x;
}
namespace seg
{
	int ls[200010];
	int rs[200010];
	int s[200010];
	int rt,n;
	void add(int &p,int x,int v,int l,int r)
	{
		if(!p)
		{
			p=++n;
			ls[p]=rs[p]=0;
			s[p]=0x7fffffff;
		}
		if(l==r)
		{
			s[p]=v;
			return;
		}
		int mid=(l+r)>>1;
		if(x<=mid)
			add(ls[p],x,v,l,mid);
		else
			add(rs[p],x,v,mid+1,r);
		s[p]=0x7fffffff;
		if(ls[p])
			upmin(s[p],s[ls[p]]);
		if(rs[p])
			upmin(s[p],s[rs[p]]);
	}
	int query(int p,int L,int R,int l,int r)
	{
		if(!p)
			return 0x7fffffff;
		if(L<=l&&R>=r)
			return s[p];
		int res=0x7fffffff;
		int mid=(l+r)>>1;
		if(L<=mid)
			upmin(res,query(ls[p],L,R,l,mid));
		if(R>mid)
			upmin(res,query(rs[p],L,R,mid+1,r));
		return res;
	}
}
struct cmp
{
	int operator ()(int a,int b) const
	{
		return d[a]<d[b];
	}
};
priority_queue<int,vector<int>,cmp> q;
int ans[100010];
int k;
int main()
{
	open("a");
	int n;
	scanf("%d",&n);
	int i,x,y;
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		g[x].push_back(y);
		g[y].push_back(x);
	}
	dfs(1,0,1);
	for(i=1;i<=n;i++)
		c[s[i]].push_back(i);
	for(i=1;i<maxd;i++)
	{
		k=i;
		int res=leaves;
		for(auto v:c[i])
			q.push(v);
		seg::rt=seg::n=0;
		while(!q.empty())
		{
			x=q.top();
			q.pop();
			if(s[x]<=k-1)
				continue;
			if(seg::query(seg::rt,st[x],ed[x],1,n)<=d[x]+k-1)
				continue;
			seg::add(seg::rt,st[x],d[x],1,n);
			res++;
			if(d[x]>k)
				q.push(jump(x,k));
		}
		ans[i]=res;
	}
	int q;
	scanf("%d",&q);
	while(q--)
	{
		scanf("%d",&x);
		if(x>=maxd)
			printf("%d\n",leaves);
		else
			printf("%d\n",ans[x]);
	}
	return 0;
}
posted @ 2018-03-06 12:48  ywwyww  阅读(367)  评论(0编辑  收藏  举报