【BZOJ】【3653】谈笑风生
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序做可持久化线段树就可以了……查询的时候就像普通线段树一样。
调了很久的一个地方……
1 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
1 /************************************************************** 2 Problem: 3653 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:8964 ms 7 Memory:158752 kb 8 ****************************************************************/ 9 10 //Huce #1 D 11 #include<vector> 12 #include<cstdio> 13 #include<cstring> 14 #include<cstdlib> 15 #include<iostream> 16 #include<algorithm> 17 #define rep(i,n) for(int i=0;i<n;++i) 18 #define F(i,j,n) for(int i=j;i<=n;++i) 19 #define D(i,j,n) for(int i=j;i>=n;--i) 20 #define pb push_back 21 using namespace std; 22 inline int getint(){ 23 int v=0,sign=1; char ch=getchar(); 24 while(ch<'0'||ch>'9'){ if (ch=='-') sign=-1; ch=getchar();} 25 while(ch>='0'&&ch<='9'){ v=v*10+ch-'0'; ch=getchar();} 26 return v*sign; 27 } 28 const int N=3e5+10,INF=~0u>>2; 29 typedef long long LL; 30 /******************tamplate*********************/ 31 int head[N],to[N<<1],next[N<<1],cnt; 32 void add(int x,int y){ 33 to[++cnt]=y; next[cnt]=head[x]; head[x]=cnt; 34 to[++cnt]=x; next[cnt]=head[y]; head[y]=cnt; 35 } 36 int n,m,a[N],dep[N],sz[N],st[N],ed[N],root[N],tot,ct; 37 struct Tree{ 38 int l,r; LL sum; 39 }t[N*30]; 40 #define mid (l+r>>1) 41 void update(int &o,int l,int r,int pos,int w){ 42 t[++ct]=t[o], o=ct, t[o].sum+=w; 43 if (l==r) return; 44 if (pos<=mid) update(t[o].l,l,mid,pos,w); 45 else update(t[o].r,mid+1,r,pos,w); 46 } 47 LL query(int i,int j,int l,int r,int ql,int qr){ 48 if (ql<=l && qr>=r){ 49 return t[j].sum-t[i].sum; 50 }else{ 51 LL ans=0; 52 if (ql<=mid) ans+=query(t[i].l,t[j].l,l,mid,ql,qr); 53 if (qr> mid) ans+=query(t[i].r,t[j].r,mid+1,r,ql,qr); 54 return ans; 55 } 56 } 57 #undef mid 58 void dfs(int x){ 59 a[st[x]=++tot]=x; 60 sz[x]=1; 61 for(int i=head[x];i;i=next[i]) 62 if (st[to[i]]==0){ 63 dep[to[i]]=dep[x]+1; 64 dfs(to[i]); 65 sz[x]+=sz[to[i]]; 66 } 67 ed[x]=tot; 68 } 69 70 int main(){ 71 #ifndef ONLINE_JUDGE 72 freopen("D.in","r",stdin); 73 freopen("D.out","w",stdout); 74 #endif 75 n=getint(); m=getint(); 76 int x,y; 77 F(i,2,n){ 78 x=getint(); y=getint(); 79 add(x,y); 80 } 81 dfs(1); 82 F(i,1,n) update(root[i]=root[i-1],0,n,dep[a[i]],sz[a[i]]-1); 83 F(i,1,m){ 84 x=getint(); y=getint(); 85 LL ans=(LL)(sz[x]-1)*min(dep[x],y); 86 ans+=query(root[st[x]-1],root[ed[x]],0,n, 87 dep[x]+1,min(dep[x]+y,n)); 88 printf("%lld\n",ans); 89 } 90 return 0; 91 } 92
3653: 谈笑风生
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 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
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