luogu P3899 [湖南集训]谈笑风生
nmyzd,mgdhls,bnmbzdgdnlql,a,wgttxfs
对于一个点\(a\),点\(b\)只有可能是他的祖先或者在\(a\)子树里
如果点\(b\)是\(a\)祖先,那么答案为a子树大小\(sz_a-1\)
否则,答案为\(sz_b-1\)
加上\(k\)的限制后,如果根节点1的深度\(de_1=1\)那么节点\(a\)的答案就是\(\sum_{b在a子树中,b\ne a,dist(a,b)\leq k}\ \ (sz_b-1)+\min(k,de_a-1)*(sz_a-1)\).后半段可以直接算,前半段的话,对每个节点开个线段树,保存子树中深度为某个值的\(\sum sz_a-1\),在遍历的时候合并线段树,查询的时候就查询区间\([de_a+1,de_a+k]\)
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
#define il inline
#define re register
#define db double
#define eps (1e-7)
using namespace std;
const int N=300000+10;
const LL inf=(1ll<<50);
il LL rd()
{
  re LL x=0,w=1;re char ch=0;
  while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
  while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
  return x*w;
}
int to[N<<1],nt[N<<1],hd[N],tot=1;
il void add(int x,int y)
{
  ++tot,to[tot]=y,nt[tot]=hd[x],hd[x]=tot;
  ++tot,to[tot]=x,nt[tot]=hd[y],hd[y]=tot;
}
LL s[N*30];
int ch[N*30][2],tt;
#define mid ((l+r)>>1)
il void psup(int o){s[o]=s[ch[o][0]]+s[ch[o][1]];}
void bui(int o,int l,int r,int lx,LL x)
{
  if(l==r){s[o]=x;return;}
  if(lx<=mid) bui(ch[o][0]=++tt,l,mid,lx,x);
  else bui(ch[o][1]=++tt,mid+1,r,lx,x);
  psup(o);
}
int merge(int o1,int o2)
{
  if(!o1) return o2;
  if(!o2||o1==o2) return o1;
  int o3=++tt;
  s[o3]=s[o1]+s[o2];
  ch[o3][0]=merge(ch[o1][0],ch[o2][0]);
  ch[o3][1]=merge(ch[o1][1],ch[o2][1]);
  return o3;
}
LL quer(int o,int l,int r,int ll,int rr)
{
  if(!o) return 0;
  if(ll<=l&&r<=rr) return s[o];
  LL an=0;
  if(ll<=mid) an+=quer(ch[o][0],l,mid,ll,rr);
  if(rr>mid) an+=quer(ch[o][1],mid+1,r,ll,rr);
  return an;
}
int n,m,rt[N];
int sz[N],de[N];
void dfs(int x,int ffa)
{
  sz[x]=1;
  for(int i=hd[x];i;i=nt[i])
    {
      int y=to[i];
      if(y==ffa) continue;
      de[y]=de[x]+1;
      dfs(y,x);
      sz[x]+=sz[y];
    }
  if(sz[x]>1)
    {
      bui(rt[x]=++tt,1,n,de[x],sz[x]-1);
      for(int i=hd[x];i;i=nt[i])
        {
          int y=to[i];
          if(y==ffa) continue;
          rt[x]=merge(rt[x],rt[y]);
        }
    }
}
int main()
{
  n=rd(),m=rd();
  for(int i=1;i<n;i++) add(rd(),rd());
  de[1]=1;
  dfs(1,0);
  while(m--)
    {
      int p=rd(),k=rd();
      printf("%lld\n",quer(rt[p],1,n,de[p]+1,de[p]+k)+1ll*(sz[p]-1)*min(de[p]-1,k));
    }
  return 0;
}
upd 12.6:
其实这题可以也可以用长链剖分,因为我们可以设\(f_{x,j}\)为点\(x\)子树内深度为\(de_x+j\)的所有点的\((sz_x-1)\)之和,转移是\(f_{x,j}=\sum_{y=son_x}f_{y,j-1}\),这个可以长链剖分后优化,直接继承重儿子状态,暴力转移轻儿子状态
处理询问和上面类似,只不过这里要把\(f_{x,j}\)搞成后缀和形式
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
#define il inline
#define re register
#define db double
#define eps (1e-7)
using namespace std;
const int N=300000+10;
const LL inf=(1ll<<50);
il LL rd()
{
  re LL x=0,w=1;re char ch=0;
  while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
  while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
  return x*w;
}
int to[N<<1],nt[N<<1],hd[N],tot=1;
il void add(int x,int y)
{
  ++tot,to[tot]=y,nt[tot]=hd[x],hd[x]=tot;
  ++tot,to[tot]=x,nt[tot]=hd[y],hd[y]=tot;
}
int n,m,fa[N],sz[N],de[N],dpt[N],hson[N];
int qk[N],qnt[N],qid[N],qhd[N],qt=1;
il void qaq(int x,int k,int id){++qt,qk[qt]=k,qnt[qt]=qhd[x],qid[qt]=id,qhd[x]=qt;}
LL an[N];
void dfs1(int x)
{
  sz[x]=1;
  for(int i=hd[x];i;i=nt[i])
    {
      int y=to[i];
      if(y==fa[x]) continue;
      fa[y]=x,dpt[y]=de[y]=de[x]+1,dfs1(y),sz[x]+=sz[y];
      if(dpt[hson[x]]<dpt[y]) hson[x]=y;
      dpt[x]=max(dpt[x],dpt[y]);
    }
}
LL *f[N],rbq[N<<1],*px=rbq+1;
void dd(int x)
{
  if(hson[x]) f[hson[x]]=f[x]+1,dd(hson[x]);
  for(int i=hd[x];i;i=nt[i])
    {
      int y=to[i];
      if(y==fa[x]||y==hson[x]) continue;
      f[y]=px,px+=dpt[y]-de[x]+1,dd(y);
      for(int i=dpt[y]-de[x]-1;i>=0;--i) f[x][i+1]+=f[y][i];
    }
  for(int i=qhd[x];i;i=qnt[i])
    {
      int k=qk[i],id=qid[i];
      an[id]=f[x][1]+1ll*(sz[x]-1)*min(de[x]-1,k);
      if(k+1<=dpt[x]-de[x]) an[id]-=f[x][k+1];
    }
  f[x][0]=f[x][1]+sz[x]-1;
}
int main()
{
  n=rd(),m=rd();
  for(int i=1;i<n;i++) add(rd(),rd());
  de[1]=1;
  dfs1(1);
  for(int i=1;i<=m;++i)
    {
      int p=rd(),k=rd();
      qaq(p,k,i);
    }
  f[1]=px,px+=dpt[1];
  dd(1);
  for(int i=1;i<=m;++i) printf("%lld\n",an[i]);
  return 0;
}
运行结果(O2):线段树合并 2022ms,长链剖分 942ms
后者不知道比前者高到哪去了,虽然不能和最优解谈笑风生
luogu P3899 [湖南集训]谈笑风生的更多相关文章
- 主席树 || 可持久化线段树 || BZOJ 3653: 谈笑风生 || Luogu P3899 [湖南集训]谈笑风生
		题面:P3899 [湖南集训]谈笑风生 题解: 我很喜欢这道题. 因为A是给定的,所以实质是求二元组的个数.我们以A(即给定的P)作为基点寻找答案,那么情况分两类.一种是B为A的父亲,另一种是A为B的 ... 
- [Luogu P3899] [湖南集训]谈笑风生 (主席树)
		题面 传送门:https://www.luogu.org/problemnew/show/P3899 Solution 你们搞的这道题啊,excited! 这题真的很有意思. 首先,我们可以先理解一下 ... 
- luogu P3899 [湖南集训]谈笑风生 线段树合并
		Code: #include<bits/stdc++.h> #define maxn 300002 #define ll long long using namespace std; vo ... 
- P3899 [湖南集训]谈笑风生
		题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=3653 https://www.luogu.org/problemnew/show/P38 ... 
- 洛谷P3899 [湖南集训]谈笑风生(线段树合并)
		题意 题目链接 Sol 线段树合并板子题,目前我看到两种写法,分别是这样的. 前一种每次需要新建一个节点,空间是\(O(4nlogn)\) 后者不需要新建,空间是\(O(nlogn)\)(面向数据算空 ... 
- Luogu 3899 [湖南集训]谈笑风生
		BZOJ 3653权限题. 这题方法很多,但我会的不多…… 给定了$a$,我们考虑讨论$b$的位置: 1.$b$在$a$到根的链上,那么这样子$a$的子树中的每一个结点(除了$a$之外)都是可以成为$ ... 
- P3899 [湖南集训]谈笑风生 主席树
		#include<iostream> #include<string.h> #include<algorithm> #include<stdio.h> ... 
- 【洛谷 P3899】 [湖南集训]谈笑风生 (主席树)
		题目链接 容易发现\(a,b,c\)肯定是在一条直链上的. 定义\(size(u)\)表示以\(u\)为根的子树大小(不包括\(u\)) 分两种情况, 1.\(b\)是\(a\)的祖先,对答案的贡献是 ... 
- bzoj 3653 [湖南集训]谈笑风生
		题目描述 设 T 为一棵有根树,我们做如下的定义: • 设 a 和 b 为 T 中的两个不同节点.如果 a 是 b 的祖先,那么称"a 比 b 不知道高明到哪里去了". • 设 a ... 
随机推荐
- 用table绘制 等宽等间距的单元
			css: .test1 { empty-cells: show;/*show:指定当表格的单元格无内容时,显示该单元格的边框.*/ border-spacing: 10px 10px;/*用长度值来定 ... 
- 2019.4.1考试&2019.4.2考试&2019.4.4考试
			4.1:T1原题,T2码农板子题,T3板子题 4.2 好像是三个出题人分别出的 以及#define *** 傻逼 T1 思维好题 转成树形DP,$dp[i][j]$表示点i值为j的方案数,记录前缀和转 ... 
- HDU/HDOJ 4699 Editor
			对顶栈算法. 此题充分说明了cin的不中以及scanf的优越性. 我TM用cin超时了!!!换成scanf就A了!!! #include <cstdio> #include <cst ... 
- MySQL存储过程实现动态执行SQL
			--存储过程名和参数,参数中in表示传入参数,out标示传出参数,inout表示传入传出参数 create procedure p_procedurecode(in sumdate varchar(1 ... 
- qml: C++调用qml函数
			C++调用qml函数,是通过下面的函数实现的: bool QMetaObject::invokeMethod(QObject *obj, const char *member, Qt::Connect ... 
- 在linux下面解压用的zxpf是什么意思,它跟zxvf有啥区别
			在linux下面解压用的zxpf是什么意思,它跟zxvf有啥区别 linux 命令中tar后跟的zxvf是什么意思:.tar.gz是一个压缩包 .tar只是打包而没有压缩 z:表示 tar 包是被 ... 
- 数据库中in和exists关键字的区别
			数据库中in和exists关键字的区别 in 是把外表和内表作hash join,而exists是对外表作loop,每次loop再对内表进行查询. 一直以来认为exists比in效率高的说法是不准确的 ... 
- 根文件系统之init
			title: 根文件系统之init tag: arm date: 2018-11-12 18:53:23 --- 引入 在Kernel源码分析中,了解到init_post是在挂载根文件系统之后执行应用 ... 
- nGrinder TestRunner XFF / X-Forwarded-For
			s 我们在压测请求报文里面带了这个"x-forward-for":"10.24.51.132"这个字段,所以我们所有的压测请求穿透到应用系统的时候,应用系统上采 ... 
- Oracle 去重并显示所有列数据
			一.原始数据(默认会生成一个 rownum 的序列,如下图的第一列) select t.* from ZD_DIC t where t.zdlx = '人员类型' 二.先分组,再给组内的内容进行排序 ... 
