Problem
BZOJ
然而是权限题,如果你像蒟蒻我一样没有权限号请上洛谷~
Solution
据说正解是主席树???
不管了,反正主席树常数巨大,线段树虽然暴力好歹也能卡过去,而且这题数据还比较水。
其实是我不会打主席树
法一
显然,分两种情况,若b是a的祖先,那么贡献就是sz[a]-1,而若b在a的子树中,则贡献为sz[b]-1。
第一种情况很好解决,考虑第二种情况。
我们将树按照dfs序拍扁成数组,然后在数组上根据dfs序中答案的最小最大深度,进行dfs搜索。注意要满足depth<=mx[rt]的限制条件才能返回区间的值,否则可能会多计算了某些深度之差大于k的值。另外要用mn[rt]来剪枝,也就是说如果区间中最小的深度都已经大于了depth,显然就不需要继续向下搜了。
然而这可以被数据卡掉,所以就介绍不会被卡的法二……
法二
Orz了一发boshi的题解
同样也是利用了dfs序,大致思想就是将询问离线,然后树形dp:维护一个序列,a[i]表示深度为i的节点的sz之和,那么答案显然就是sum[a[i]+k]-sum[a[i]],考虑用树状数组维护,退出该节点时插入此节点的贡献即可。答案不能受非子树,深度符合要求的贡献影响,在进入节点时减去其贡献即可。
时间复杂度
O(nlogn)
O
(
n
l
o
g
n
)
Code
#include <cstdio>
using namespace std;
typedef long long ll;
template <typename Tp> inline void read(Tp &x)
{
x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
const int maxn=300010;
struct data{int v,nxt;}edge[maxn<<1];
int n,m,p,dfc,sz[maxn],deep[maxn],dfn[maxn][2],head[maxn];
int xu[maxn<<1],a[maxn<<1],mx[maxn<<3],mn[maxn<<3];
ll ans,sum[maxn<<3];
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline void insert(int u,int v)
{
edge[++p]=(data){v,head[u]};head[u]=p;
edge[++p]=(data){u,head[v]};head[v]=p;
}
void dfs(int x,int pre)
{
dfn[x][0]=++dfc;sz[x]=1;
for(int i=head[x];i;i=edge[i].nxt)
if(edge[i].v!=pre)
{
deep[edge[i].v]=deep[x]+1;
dfs(edge[i].v,x);
sz[x]+=sz[edge[i].v];
}
dfn[x][1]=++dfc;
xu[dfn[x][0]]=xu[dfn[x][1]]=deep[x];
a[dfn[x][0]]=sz[x]-1;a[dfn[x][1]]=0;
}
void build(int l,int r,int rt)
{
if(l==r){mx[rt]=mn[rt]=xu[l];sum[rt]=a[l];return ;}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
mx[rt]=max(mx[rt<<1],mx[rt<<1|1]);
mn[rt]=min(mn[rt<<1],mn[rt<<1|1]);
}
ll query(int l,int r,int L,int R,int now,int depth)
{
if(mn[now]>depth) return 0;
if(L<=l&&r<=R&&mx[now]<=depth) return sum[now];
int m=(l+r)>>1;ll res=0;
if(L<=m) res+=query(l,m,L,R,now<<1,depth);
if(m<R) res+=query(m+1,r,L,R,now<<1|1,depth);
return res;
}
int main()
{
read(n);read(m);
for(int i=1,x,y;i<n;i++) {read(x);read(y);insert(x,y);}
deep[1]=1;dfs(1,0);
build(1,dfc,1);
for(int i=1,x,y;i<=m;i++)
{
read(x),read(y);
ans=(ll)min(deep[x]-1,y)*(sz[x]-1);
ans+=query(1,dfc,dfn[x][0]+1,dfn[x][1],1,deep[x]+y);
printf("%lld\n",ans);
}
return 0;
}
boshi’s code
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#define MX 300003
using namespace std;
typedef long long ll;
int fst[MX],nxt[MX*2],v[MX*2],siz[MX],dep[MX],lnum;
ll sum[MX];
vector<int>qk[MX],qid[MX];
ll ans[MX];
int n,m;
void add(int p,ll x){for(;p<MX;p+=(p&(-p)))sum[p]+=x;}
ll qsm(int p){ll x=0;for(;p;p-=(p&(-p)))x+=sum[p];return x;}
void addeg(int nu,int nv)
{
nxt[++lnum]=fst[nu];
fst[nu]=lnum;
v[lnum]=nv;
}
void dfs(int x,int fa,int d)
{
siz[x]=1,dep[x]=d;
for(int i=fst[x];i!=-1;i=nxt[i])
if(v[i]!=fa)
dfs(v[i],x,d+1),
siz[x]+=siz[v[i]];
}
void calc(int x,int fa)
{
for(int i=0;i<qk[x].size();i++)ans[qid[x][i]]-=(qsm(min(dep[x]+qk[x][i],MX-1))-qsm(dep[x]));
for(int i=fst[x];i!=-1;i=nxt[i])
if(v[i]!=fa)
calc(v[i],x);
for(int i=0;i<qk[x].size();i++)ans[qid[x][i]]+=(qsm(min(dep[x]+qk[x][i],MX-1))-qsm(dep[x])+(ll)min(dep[x]-1,qk[x][i])*(ll)(siz[x]-1));
add(dep[x],siz[x]-1);
}
void input()
{
int a,b;
memset(fst,0xff,sizeof(fst)),lnum=-1;
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
addeg(a,b);
addeg(b,a);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
qk[a].push_back(b);
qid[a].push_back(i);
}
}
int main()
{
input();
dfs(1,0,1);
calc(1,0);
for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
return 0;
}