【2014-7】day3 T2 谈笑风生



首先考虑给定的a点,那么b点的位置只可能是a的祖先或者a的不大于k层的儿子

对于祖先,答案很好统计,而对于儿子,就要考虑维护dfs序

对于给定的p,k,儿子部分的答案为整颗子树中深度不大于dep[p]+k所有sz[v]-1的和


所以很容易想到维护一颗树套树或者可持久化线段树


但是数据范围有30w,树套树肯定会超时,可持久化线段树常数也很大(即标程)

所以我的做法就在这里考虑离线处理

我们对所有询问的dep[p]+k排序,依次加点即可

神奇的是,我的暴力速度在随机数据下和我的程序差不到一倍,可惜被卡了。。。。。。。。。。。。。

速度比标程快一倍


注意dfs的时候需要手工栈



#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <stack>
using namespace std;
#define rep(i,l,r) for(int i=(l),_=(r);i<=_;i++)
#define per(i,r,l) for(int i=(r),_=(l);i>=_;i--)
#define MS(arr,x) memset(arr,x,sizeof(arr))
#define INE(i,u) for(int i=head[u];~i;i=e[i].next)
#define LL long long
inline const int read()
{int r=0,k=1;char c=getchar();for(;c<'0'||c>'9';c=getchar())if(c=='-')k=-1;
for(;c>='0'&&c<='9';c=getchar())r=r*10+c-'0';return k*r;}

const int N=310010;
int n,q;
struct edge{int v,next;}e[N*2];
int head[N],k;

int fa[N],dep[N],sz[N];
int in[N],out[N],dfs_clock;
struct query{int p,k,id;}qry[N];
LL ans[N];
int dhead[N],pt[N],next[N],kk;

LL sum[N*2];

LL getsum(int l,int r)
{
	LL s=0;
	for(;r;r-=r&-r) s+=sum[r];
	for(l--;l;l-=l&-l) s-=sum[l];
	return s;
}
void insert(int o,int x)
{
	for(;o<=2*n;o+=o&-o) sum[o]+=x;
}
void add(int d)
{
	for(int i=dhead[d];~i;i=next[i])
	    insert(in[pt[i]],sz[pt[i]]-1);
}
bool cmp(query a,query b){return dep[a.p]+a.k < dep[b.p]+b.k;}
void adde(int u,int v){e[k]=(edge){v,head[u]};head[u]=k++;}
void dfs()
{
	stack<int>s;
	s.push(1);
	while(!s.empty())
	{
		int u=s.top(),f=0;
		if(!in[u]) in[u]=++dfs_clock;
		sz[u]=1;
		INE(i,u)
		{
			int v=e[i].v; if(v==fa[u] || in[v]) continue;
			fa[v]=u;
			dep[v]=dep[u]+1;
			s.push(v);
			f=1;
		}
		if(!f)
		{
			out[u]=++dfs_clock;
			INE(i,u)
			{
				int v=e[i].v; if(v==fa[u]) continue;
				sz[u]+=sz[v];
			}
			s.pop();
		}
	}
}
void init_d()
{
	rep(i,1,n)
	{
		int d=dep[i];
		kk++;
		pt[kk]=i;
		next[kk]=dhead[d];
		dhead[d]=kk;
	}
}

void input()
{
	MS(head,-1); MS(dhead,-1);
    cin>>n>>q;
    rep(i,1,n-1)
    {
    	int u=read(), v=read();
    	adde(u,v); adde(v,u);
    }
}
void solve()
{
	dfs();
	init_d();
	rep(i,1,q)
	{
		int p=read(), k=read();
		qry[i]=(query){p,k,i};
		ans[i]+=(LL)min(dep[p],k) * (sz[p]-1);
	}
	sort(&qry[1],&qry[q+1],cmp);
	int cur=0;
	rep(i,1,q)
	{
		while(cur<dep[qry[i].p]+qry[i].k) add(++cur);
		if(in[qry[i].p]+1 <= out[qry[i].p]-1)
		    ans[qry[i].id]+=getsum(in[qry[i].p]+1,out[qry[i].p]-1);
	}
	rep(i,1,q) printf("%I64d\n",ans[i]);
}

int main()
{
    freopen("laugh.in","r",stdin); freopen("laugh.out","w",stdout);
    input(),solve();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值