luoguP3806 【模板】点分治1 [点分治]

题目背景

感谢hzwer的点分治互测。

题目描述

给定一棵有n个点的树

询问树上距离为k的点对是否存在。

输入输出格式

输入格式:

n,m 接下来n-1条边a,b,c描述a到b有一条长度为c的路径

接下来m行每行询问一个K

输出格式:

对于每个K每行输出一个答案,存在输出“AYE”,否则输出”NAY”(不包含引号)

输入输出样例

输入样例#1:

2 1
1 2 2
2

输出样例#1:

AYE

说明

对于30%的数据n<=100

对于60%的数据n<=1000,m<=50

对于100%的数据n<=10000,m<=100,c<=1000,K<=10000000



套用点分治的模板。

设num[k]为树中长度为k的路径的出现次数。

对于一个点no,可以dfs遍历一遍以它为根的子树中、以它开头向下的链长度。

对于每一对以no开头的链长度d[i],d[j],num[d[i]+d[j]]++即可。

当然要记得容斥,因为要去掉d[i],d[j]表示的链来自no的同一个儿子。

设no与一个儿子相连边的长度为dis,在递归处理这个儿子的时候,对儿子的每一对d[i],d[j],做num[d[i]+d[j]+dis*2]--即可。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #define rint register int
  5 using namespace std;
  6
  7 int read(){
  8     char ch;
  9     int re=0;
 10     bool flag=0;
 11     while((ch=getchar())!=‘-‘&&(ch<‘0‘||ch>‘9‘));
 12     ch==‘-‘?flag=1:re=ch-‘0‘;
 13     while((ch=getchar())>=‘0‘&&ch<=‘9‘)  re=re*10+ch-‘0‘;
 14     return flag?-re:re;
 15 }
 16
 17 struct Edge{
 18     int to,nxt,w;
 19     Edge(int to=0,int nxt=0,int w=0):
 20         to(to),nxt(nxt),w(w){}
 21 };
 22
 23 const int maxn=10005;
 24
 25 int n,m,cnt=0,sum,tot,root;
 26 int head[maxn],son[maxn],F[maxn],num[10000005],d[maxn];
 27 bool vis[maxn];
 28 Edge E[maxn<<1];
 29
 30 inline void a_ed(int from,int to,int w){
 31     E[++cnt]=Edge(to,head[from],w);
 32     head[from]=cnt;
 33     E[++cnt]=Edge(from,head[to],w);
 34     head[to]=cnt;
 35 }
 36
 37 void init(){
 38     n=read();  m=read();
 39     for(rint i=1,from,to,w;i<n;i++){
 40         from=read();  to=read();  w=read();
 41         a_ed(from,to,w);
 42     }
 43 }
 44
 45 void getroot(int no,int fa){
 46     son[no]=1;  F[no]=0;
 47     for(rint e=head[no];e;e=E[e].nxt){
 48         int nt=E[e].to;
 49         if(nt==fa||vis[nt])  continue;
 50         getroot(nt,no);
 51         son[no]+=son[nt];
 52         F[no]=max(F[no],son[nt]);
 53     }
 54     F[no]=max(F[no],sum-son[no]);
 55     if(F[no]<F[root])  root=no;
 56 }
 57
 58 void getdeep(int no,int fa,int dd){
 59     d[tot++]=dd;
 60     for(rint e=head[no];e;e=E[e].nxt){
 61         int nt=E[e].to;
 62         if(nt==fa||vis[nt])  continue;
 63         getdeep(nt,no,dd+E[e].w);
 64     }
 65 }
 66
 67 void calc(int no,bool opt,int p){
 68     tot=0;
 69     getdeep(no,0,0);
 70     for(int i=0;i<tot;i++)
 71         for(int j=0;j<tot;j++)
 72             if(opt)  num[d[i]+d[j]]++;
 73             else  num[d[i]+d[j]+p]--;
 74 }
 75
 76 void solve(int no){
 77     vis[no]=1;
 78     calc(no,1,0);
 79     for(rint e=head[no];e;e=E[e].nxt){
 80         int nt=E[e].to;
 81         if(vis[nt])  continue;
 82         calc(nt,0,E[e].w<<1);
 83         sum=son[nt];  root=0;
 84         getroot(nt,0);
 85         solve(nt);
 86     }
 87 }
 88
 89 int main(){
 90     init();
 91     sum=F[root=0]=n;
 92     getroot(1,0);
 93     solve(root);
 94     for(rint i=0,q;i<m;i++){
 95         q=read();
 96         if(num[q])  puts("AYE");
 97         else  puts("NAY");
 98     }
 99     return 0;
100 }
时间: 2024-12-30 12:58:59

luoguP3806 【模板】点分治1 [点分治]的相关文章

点分治+动态点分治

最近好颓废,什么都学不进去... 感谢两篇:AKMer - 浅谈树分治  言简意赅 LadyLex - 点分治&动态点分治小结  讲解+例题,学到很多东西 点分治 动态点分治 ~ 点分治 ~ 经常遇见一类树上的计数题,问的是在某些条件下,选择一些点的方案数 若对于每个点的统计都需要遍历以其为根节点的子树,普通的做法就是$O(n^2)$的,在很多时候是不满足要求的 而这是点分治的专长 点分治是这样进行的: 1. 找到当前树的重心 2. 将重心及重心连出的边全部删去,那么就能将原来的树分割成森林 3

COGS 2479. [HZOI 2016]偏序 [CDQ分治套CDQ分治 四维偏序]

传送门 给定一个有n个元素的序列,元素编号为1~n,每个元素有三个属性a,b,c,求序列中满足i<j且ai<aj且bi<bj且ci<cj的数对(i,j)的个数. 对于100%的数据,1<=n<=50000,保证所有的ai.bi.ci分别组成三个1~n的排列. $CDQ$分治套$CDQ$分治也不是很难嘛 对于本题,设四维$a,b,c,d$ $Sort\ at\ a$ $CDQ(l,r)$ $\quad CDQ(l,mid)$ $\quad CDQ(mid+1,r)$ $\

POJ 1741 树分治(点分治模板题)

POJ 1741 题意:求一棵树中点对<=k的数量. 总结:点分治,搞不太懂..大概敲了一遍 #include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stac

[模板] 多项式: 乘法/求逆/分治fft/微积分/ln/exp/幂

多项式 代码 const int nsz=(int)4e5+50; const ll nmod=998244353,g=3,ginv=332748118ll; //basic math ll qp(ll a,ll b){ ll res=1; for(;b;a=a*a%nmod,b>>=1)if(b&1)res=res*a%nmod; return res; } ll inv(ll n){ return qp(n,nmod-2); } //polynomial operations //

POJ 1741 Tree 树分治(点分治)

题意:给你一颗带边权树,问你其中 dis(v,u) <= k 的对数 解题思路: 首先推荐大家看 09年国家集训队漆子超 的论文 看到这题  我们可以有三种思路 第一种是枚举起点,遍历整颗树找对数    时间复杂度 为  O(n^2),空间复杂度为 O(n) 第二种是树形dp的思想     每个节点用 长度为 K 数组维护 ,递归求解  时间复杂度为 O(n ^k)空间复杂度 为 O(n) 第三种就是我们要用到的点分治的思想. 这种思想的具体思路是  先找到一个  根  对这个根进行 深搜, 找

【cdq分治】cdq分治与整体二分学习笔记Part1.整体二分

之所以把cdq分治和整体二分放在一起学习,是因为他们两个实在太像了-不管是做法还是代码- 感觉整体二分可能会比cdq分治稍微简单那么一点点?所以先学整体二分.(感觉他们的区别在于整体二分是对每个操作二分答案,cdq是分治了操作序列) 整体二分是对答案进行二分,其具体操作如下: (比如以ZJOJ2013K大数查询为例) 具体过程 Step1.从(L,R)二分答案.mid=(L+R)>>1,用线段树维护原序列中(a,b)位置比mid大的数有多少个,同时记录对序列的操作分别是什么操作. Step2.

树的分治之点分治

点分治我感觉是图论树部分比较考验脑力的一种题目了 POJ1741 题意:给一棵边带权树,问两点之间的距离小于等于K的点对有多少个 满足条件的点对有两种情况:两个点的路径横跨树根,两个点位于同一颗子树中 对于根节点进行一次dfs,求出deep,并将其从小到大排序 void getdeep(int x,int fa) { dep[++dep[0]]=d[x]; for(int tmp=g[x];tmp;tmp=e[tmp].next) { if(e[tmp].t==fa||vis[e[tmp].t]

bzoj 3672 利用点分治将CDQ分治推广到树型结构上

最大的收获就是题目所说. deal(s) : 处理节点s所在块的问题,并保证: 1.s是该块中最靠近根节点的点,没有之一. 2.s所在块到根节点的路径上的点全都用来更新过了s所在块的所有节点. 然后步骤是: 1.找s所在块的重心c. 2.如果s就是c,那么用c更新当前块的所有节点,然后“删除c”,递归处理新产生的子块. 3.否则,删除c,deal(s),用c到s的路径(不包括c,包括s)更新c除了s子块的其他子块以及c,然后再用c去更新一次. 4.递归处理其它子块. 1 #include <cs

poj2114 树分治(点分治)

poj1741板子套一套,统计对数的方式改一下,可以在O(n)时间内统计对数 最后不要忘记输出最后的"." /* 给定一棵边权树,是否存在一条路径使得其长度为恰好为x 把1741的板子改为求点对之间的距离=k的点对数即可 */ #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define MAXN 10010 using namespac