您的位置:首页 > 其它

【BZOJ】【3653】谈笑风生

2015-04-18 23:38 423 查看

dfs序+可持久化线段树

  好吧……是我too naive

  这题……$$ans=min(dep[x],k)×(size[x]-1)+\sum_{y在x的子树中,且dis(x,y)<=k}(size[y]-1)$$

  那么重点是后面sigma的部分,这里看到子树中信息的统计可以用dfs序……但是对子树中dep在某个范围内的点的size求和?

  我们可以用权值线段树呀~dep做关键字,size的和是线段树上统计的额外信息,那么对整个dfs序做可持久化线段树就可以了……查询的时候就像普通线段树一样。

调了很久的一个地方……

F(i,1,n) update(root[i]=root[i-1],0,n,dep[a[i]],sz[a[i]]-1);

这一句中我一开始写成.......dep[a[x]],sz[a[x]]-1了!!

因为在dfs的过程中我是用x这个变量来表示某个节点的……然后这里顺着思路就写下来了……

你可能会说,为什么没有CE呢?那是因为我在读入边的时候声明了两个变量x,y……看代码上下文就知道了TAT

/**************************************************************
Problem: 3653
User: Tunix
Language: C++
Result: Accepted
Time:8964 ms
Memory:158752 kb
****************************************************************/

//Huce #1 D
#include<vector>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define rep(i,n) for(int i=0;i<n;++i)
#define F(i,j,n) for(int i=j;i<=n;++i)
#define D(i,j,n) for(int i=j;i>=n;--i)
#define pb push_back
using namespace std;
inline int getint(){
int v=0,sign=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if (ch=='-') sign=-1; ch=getchar();}
while(ch>='0'&&ch<='9'){ v=v*10+ch-'0'; ch=getchar();}
return v*sign;
}
const int N=3e5+10,INF=~0u>>2;
typedef long long LL;
/******************tamplate*********************/
int head
,to[N<<1],next[N<<1],cnt;
void add(int x,int y){
to[++cnt]=y; next[cnt]=head[x]; head[x]=cnt;
to[++cnt]=x; next[cnt]=head[y]; head[y]=cnt;
}
int n,m,a
,dep
,sz
,st
,ed
,root
,tot,ct;
struct Tree{
int l,r; LL sum;
}t[N*30];
#define mid (l+r>>1)
void update(int &o,int l,int r,int pos,int w){
t[++ct]=t[o], o=ct, t[o].sum+=w;
if (l==r) return;
if (pos<=mid) update(t[o].l,l,mid,pos,w);
else update(t[o].r,mid+1,r,pos,w);
}
LL query(int i,int j,int l,int r,int ql,int qr){
if (ql<=l && qr>=r){
return t[j].sum-t[i].sum;
}else{
LL ans=0;
if (ql<=mid) ans+=query(t[i].l,t[j].l,l,mid,ql,qr);
if (qr> mid) ans+=query(t[i].r,t[j].r,mid+1,r,ql,qr);
return ans;
}
}
#undef mid
void dfs(int x){
a[st[x]=++tot]=x;
sz[x]=1;
for(int i=head[x];i;i=next[i])
if (st[to[i]]==0){
dep[to[i]]=dep[x]+1;
dfs(to[i]);
sz[x]+=sz[to[i]];
}
ed[x]=tot;
}

int main(){
#ifndef ONLINE_JUDGE
freopen("D.in","r",stdin);
freopen("D.out","w",stdout);
#endif
n=getint(); m=getint();
int x,y;
F(i,2,n){
x=getint(); y=getint();
add(x,y);
}
dfs(1);
F(i,1,n) update(root[i]=root[i-1],0,n,dep[a[i]],sz[a[i]]-1);
F(i,1,m){
x=getint(); y=getint();
LL ans=(LL)(sz[x]-1)*min(dep[x],y);
ans+=query(root[st[x]-1],root[ed[x]],0,n,
dep[x]+1,min(dep[x]+y,n));
printf("%lld\n",ans);
}
return 0;
}


View Code

3653: 谈笑风生

Time Limit: 20 Sec Memory Limit: 512 MB
Submit: 225 Solved: 79
[Submit][Status][Discuss]

Description

设T 为一棵有根树,我们做如下的定义:

• 设a和b为T 中的两个不同节点。如果a是b的祖先,那么称“a比b不知道

高明到哪里去了”。

• 设a 和 b 为 T 中的两个不同节点。如果 a 与 b 在树上的距离不超过某个给定

常数x,那么称“a 与b 谈笑风生”。

给定一棵n个节点的有根树T,节点的编号为1 到 n,根节点为1号节点。你需

要回答q 个询问,询问给定两个整数p和k,问有多少个有序三元组(a;b;c)满足:

1. a、b和 c为 T 中三个不同的点,且 a为p 号节点;

2. a和b 都比 c不知道高明到哪里去了;

3. a和b 谈笑风生。这里谈笑风生中的常数为给定的 k。

Input

输入文件的第一行含有两个正整数n和q,分别代表有根树的点数与询问的个数。接下来n - 1行,每行描述一条树上的边。每行含有两个整数u和v,代表在节点u和v之间有一条边。

接下来q行,每行描述一个操作。第i行含有两个整数,分别表示第i个询问的p和k。

Output

输出 q 行,每行对应一个询问,代表询问的答案。

Sample Input

5 3

1 2

1 3

2 4

4 5

2 2

4 1

2 3

Sample Output

3

1

3

HINT

1<=P<=N

1<=K<=N

N<=300000

Q<=300000

Source

[Submit][Status][Discuss]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: