洛谷P3806 点分治1 & POJ1741 Tree & CF161D Distance in Tree
正解:点分治
解题报告:
点分治板子有点多,,,分开写题解的话就显得很空旷,不写又不太好毕竟初学还是要多写下题解便于理解
于是灵巧发挥压行选手习惯,开始压题解(bushi
不扯辣,按顺序港下这几道题QwQ
T1
就港两个方面趴,一个是怎么找重心一个是怎么分治
对于怎么找重心,直接dfs,然后记录每个子树的size,因为重心的定义是sizemaxmin,所以存一下sizemax然后比较一下就好了
然后怎么分治呢
根据点分治的定义,我们就是以重心为根然后分开治理各个子树,最后只要处理的就是经过重心点的路径辣
然后对于经过重心的点的距离(u,v)就=dis(u,root)+dis(root,v)
然后开个桶记录下能构成的长度就可以了
然后对于询问也开个桶存能否达到
这样复杂度就是O(nlogn)的
昂然后说一下,如果有只WA在第五个点的注意一下,就是在查某个询问能否达到的时候,要先判断询问和当前边的长度的大小关系(具体见代码中count函数),不然可能数组越界,但这题很迷的是数组越界不会报RE,而是告诉你,WA辣,就可能比较难找出来,加个判断就好QwQ
然后如果不开桶好像也能做,,,就是复杂度O(n2logn)的
不开询问桶(但是也开了桶记录能构成的长度),只是枚举的不是询问而是点(因为m<=100而n<=1000所以上面那个方法复杂度好看一些),就直接for循环两两枚举点就好
也是能过的QwQ
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define rg register
#define gc getchar()
#define ll long long
#define rp(i,x,y) for(rg ll i=x;i<=y;++i)
#define e(i,x) for(rg ll i=head[x];i;i=edge[i].nxt) const ll N=+,M=+,inf=+;
ll n,m,cnt,ques[M],sz[N],mxsz[N],sum,que[N],head[N],rt,rem[N],dis[N];
bool ext[M],jud[inf],vis[N];
struct ed{ll to,nxt,wei;}edge[N<<]; il ll read()
{
rg char ch=gc;rg ll x=;rg bool y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
il void ad(ll x,ll y,ll z){edge[++cnt]=(ed){x,head[y],z};head[y]=cnt;}
il void dfs(ll x,ll fa)
{
sz[x]=;mxsz[x]=;
e(i,x){if(edge[i].to==fa || vis[edge[i].to])continue;dfs(edge[i].to,x);sz[x]+=sz[edge[i].to];mxsz[x]=max(mxsz[x],sz[edge[i].to]);}
mxsz[x]=max(mxsz[x],sum-sz[x]);if(mxsz[x]<mxsz[rt])rt=x;
}
il void dfs2(ll x,ll fa){rem[++rem[]]=dis[x];e(i,x){if(vis[edge[i].to] || edge[i].to==fa)continue;dis[edge[i].to]=dis[x]+edge[i].wei;dfs2(edge[i].to,x);}}
il void count(ll x)
{
cnt=;
e(i,x)
{
if(vis[edge[i].to])continue;
rem[]=;dis[edge[i].to]=edge[i].wei;dfs2(edge[i].to,x);
rp(j,,rem[])rp(k,,m)if(ques[k]>=rem[j])ext[k]|=jud[ques[k]-rem[j]];
rp(j,,rem[])jud[que[++cnt]=rem[j]]=;
}
rp(i,,cnt)jud[que[i]]=;
}
il void solv(ll x)
{
// printf("rt=%lld\n",x);
vis[x]=jud[]=;count(x);
e(i,x){if(vis[edge[i].to])continue;sum=sz[edge[i].to],mxsz[rt=]=inf;dfs(edge[i].to,);solv(rt);}
} int main()
{
n=read();m=read();rp(i,,n-){ll x=read(),y=read(),z=read();ad(x,y,z);ad(y,x,z);}rp(i,,m)ques[i]=read();
mxsz[rt]=sum=n;dfs(,);solv(rt);rp(i,,m)if(ext[i])printf("AYE\n");else printf("NAY\n");
return ;
}
这是$O(nlogn^{2})$的代码QwQ
T2
其实和T1差不多嘛,,,唯一的区别就是它要求的是<=,考虑怎么实现?
开个桶+树状数组(用平衡树什么的当然也能过去,,,)维护一下就好
然后就麻油什么新意了,差不多QwQ
昂然后其实这题也可以不用树状数组,考虑双指针,这个小技巧还挺有意思的有时间专门写下?
然后怎么实现,,,挺简单的看代码趴懒得港辣,two pointer这个东西其实还挺好理解的,只是有时候比较难想到而已,实现也real简单(感jio比树状数组简单实现些!(虽然也跑得慢些,,,QAQ)(加了一堆rg+吸氧之后海星QwQ
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define rg register
#define gc getchar()
#define ll int
#define rp(i,x,y) for(rg ll i=x;i<=y;++i)
#define e(i,x) for(rg ll i=head[x];i;i=edge[i].nxt) const ll N=+;
ll n,m,cnt,sz[N],mxsz[N],sum,head[N],rt,dis[N],as,gdgs,nodecnt,num[N];
bool vis[N];
struct ed{ll to,nxt,wei;}edge[N<<];
struct node{ll dis,bl;}nod[N]; il ll read()
{
rg char ch=gc;rg ll x=;rg bool y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc; if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
il void ad(rg ll x,rg ll y,rg ll z){edge[++cnt]=(ed){x,head[y],z};head[y]=cnt;}
il void dfs(rg ll x,rg ll fa)
{
sz[x]=;mxsz[x]=;
e(i,x){if(edge[i].to==fa || vis[edge[i].to])continue;dfs(edge[i].to,x);sz[x]+=sz[edge[i].to];mxsz[x]=max(mxsz[x],sz[edge[i].to]);}
mxsz[x]=max(mxsz[x],sum-sz[x]);if(mxsz[x]<mxsz[rt])rt=x;
}
il bool cmp(rg node gd,rg node gs){return gd.dis<gs.dis;}
il void dfs2(rg ll x,rg ll fa,rg ll son){nod[++nodecnt]=(node){dis[x],son};++num[son];e(i,x){if(vis[edge[i].to] || edge[i].to==fa)continue;dis[edge[i].to]=dis[x]+edge[i].wei;dfs2(edge[i].to,x,son);}}
il void count(rg ll x)
{
cnt=;nodecnt=;nod[++nodecnt]=(node){,x};
e(i,x){if(vis[edge[i].to])continue;dis[edge[i].to]=edge[i].wei;dfs2(edge[i].to,x,edge[i].to);}sort(nod+,nod+nodecnt+,cmp);ll l=,r=nodecnt;
while(l<r){while(l<r && nod[l].dis+nod[r].dis>gdgs)--num[nod[r--].bl];if(l<r)as+=r-l-num[nod[l].bl],num[nod[++l].bl]--;}
}
il void solv(rg ll x)
{
vis[x]=;count(x);
e(i,x){if(vis[edge[i].to])continue;sum=sz[edge[i].to],mxsz[rt=]=N;dfs(edge[i].to,);solv(rt);}
} int main()
{
n=read();rp(i,,n-){ll x=read(),y=read(),z=read();ad(x,y,z);ad(y,x,z);} gdgs=read();mxsz[rt]=sum=n;dfs(,);solv(rt);printf("%d\n",as); return ;
}
T3
和T1T2都差不多嘛,不要小于等于就更简单辣不用树状数组直接做就好了
没了
然后我发现直接做好像会超时,,,?(也可能是我代码太丑陋辣呜呜呜,,,
所以get一个新方法,用双指针(这个小技巧是真挺有用的欸,,,教练我想学$bushi$
先对dis排序,然后用双指针lr,显然在排序之后能保证它不会跳来跳去而是一直往后走(有点儿像莫队?大概理解一下就好,具体看代码趴QAQ)
这样就从O(n2)降到了O(n)!
对了这题可以用树形dp,,,大概港下
f[x][i]:点x走i步能到达的点的个数
转移也很好想鸭,f[x][i]+=f[edge[x].to][i-1]
最后统计答案有两种,一种直接f[x][k],一种是f[edge[x].to][i-1]*(f[x][K-i]-f[edge[x].to][k-i-1]),即以x为lca的点对,这里注意因为(u,v)与(v,u)算一种,所以第二种要/2
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define rg register
#define gc getchar()
#define ll long long
#define rp(i,x,y) for(rg ll i=x;i<=y;++i)
#define e(i,x) for(rg ll i=head[x];i;i=edge[i].nxt) const ll N=+;
ll n,m,cnt,sz[N],mxsz[N],sum,head[N],rt,dis[N],as,gdgs,nodecnt,num[N];
bool vis[N];
struct ed{ll to,nxt;}edge[N<<];
struct node{ll dis,bl;}nod[N]; il ll read()
{
rg char ch=gc;rg ll x=;rg bool y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc; if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
il void ad(ll x,ll y){edge[++cnt]=(ed){x,head[y]};head[y]=cnt;}
//il void updat(ll x,ll val){while(x<=gdgs)tr[x]+=val,x+=lowbit(x);}
//il ll query(ll x){ll as=0;while(x)as+=tr[x];return as;}
il bool cmp(node gd,node gs){return gd.dis<gs.dis;}
il void dfs(ll x,ll fa)
{
sz[x]=;mxsz[x]=;
e(i,x){if(edge[i].to==fa || vis[edge[i].to])continue;dfs(edge[i].to,x);sz[x]+=sz[edge[i].to];mxsz[x]=max(mxsz[x],sz[edge[i].to]);}
mxsz[x]=max(mxsz[x],sum-sz[x]);if(mxsz[x]<mxsz[rt])rt=x;
}
il void dfs2(ll x,ll fa,ll son){nod[++nodecnt]=(node){dis[x],son};e(i,x){if(vis[edge[i].to] || edge[i].to==fa)continue;dis[edge[i].to]=dis[x]+;dfs2(edge[i].to,x,son);}}
il void count(ll x)
{
cnt=;nodecnt=;nod[++nodecnt]=(node){,x};
e(i,x){if(vis[edge[i].to])continue;dis[edge[i].to]=;dfs2(edge[i].to,x,edge[i].to);}sort(nod+,nod+nodecnt+,cmp);ll l=,r=nodecnt,tmp=r;
while(l<r)
{
if(l== || nod[l].dis!=nod[l-].dis)
{
while(r>tmp)--num[nod[r--].bl];cnt=;
while(l<r && nod[l].dis+nod[r].dis>gdgs)--r;
if(l>=r)break;tmp=r;
while(l<tmp && nod[l].dis+nod[tmp].dis==gdgs)++cnt,++num[nod[tmp--].bl];
}
as+=cnt-num[nod[l].bl];if(tmp==l)--num[nod[++tmp].bl],--cnt;++l;
}
}
il void solv(ll x)
{
vis[x]=;count(x);
e(i,x){if(vis[edge[i].to])continue;sum=sz[edge[i].to],mxsz[rt=]=N;dfs(edge[i].to,);solv(rt);}
} int main()
{
n=read();gdgs=read();rp(i,,n-){ll x=read(),y=read();ad(x,y);ad(y,x);} mxsz[rt]=sum=n;dfs(,);solv(rt);printf("%lld\n",as); return ;
}
改了1h的点分治代码!
洛谷P3806 点分治1 & POJ1741 Tree & CF161D Distance in Tree的更多相关文章
- 洛谷 P3806 点分治模板
题目:https://www.luogu.org/problemnew/show/P3806 就是点分治~ 每次暴力枚举询问即可,复杂度是 nmlogn: 注意 tmp[0]=1 ! 代码如下: #i ...
- 洛谷P3806 点分治
点分治 第一次写点分治..感觉是一个神奇而又暴力的东西orz 点分治大概就是用来处理树上链的信息,把路径分成过点x和不过点x的两种,不过点x的路径可以变成过点x的子树中一点的路径,递归处理 #incl ...
- [洛谷P3806] [模板] 点分治1
洛谷 P3806 传送门 这个点分治都不用减掉子树里的了,直接搞就行了. 注意第63行 if(qu[k]>=buf[j]) 不能不写,也不能写成>. 因为这个WA了半天...... 如果m ...
- CF161D Distance in Tree
CF161D Distance in Tree LG传送门 长链剖分板子题. 长链剖分那么好写,跑得又快,为什么要写点分治呢?完了我现在看一道点分治题就想写长链剖分 如果还不会长链剖分请看我博客. 没 ...
- POJ 1741.Tree and 洛谷 P4178 Tree-树分治(点分治,容斥版) +二分 模板题-区间点对最短距离<=K的点对数量
POJ 1741. Tree Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 34141 Accepted: 11420 ...
- BZOJ3262/洛谷P3810 陌上花开 分治 三维偏序 树状数组
原文链接http://www.cnblogs.com/zhouzhendong/p/8672131.html 题目传送门 - BZOJ3262 题目传送门 - 洛谷P3810 题意 有$n$个元素,第 ...
- 【算法学习】【洛谷】cdq分治 & P3810 三维偏序
cdq是何许人也?请参看这篇:https://wenku.baidu.com/view/3b913556fd0a79563d1e7245.html. 在这篇论文中,cdq提出了对修改/询问型问题(Mo ...
- 洛谷 P3806 【模板】点分治1
P3806 [模板]点分治1 题目背景 感谢hzwer的点分治互测. 题目描述 给定一棵有n个点的树 询问树上距离为k的点对是否存在. 输入输出格式 输入格式: n,m 接下来n-1条边a,b,c描述 ...
- 洛谷 P3806 【模板】点分治1-树分治(点分治,容斥版) 模板题-树上距离为k的点对是否存在
P3806 [模板]点分治1 题目背景 感谢hzwer的点分治互测. 题目描述 给定一棵有n个点的树 询问树上距离为k的点对是否存在. 输入格式 n,m 接下来n-1条边a,b,c描述a到b有一条长度 ...
随机推荐
- iOS开发--画一条黑色的横线
在网上搜索了下大概有下面几种方法: 1.使用Quartz2D画出横线 需要一个UIVIew把这两个Label装起来,你需要计算好他们的位置同时给黑线预留像素的位置.这样你在UIView里面- (voi ...
- iOS UIImage:获取图片主色调
本文转载至 http://www.wahenzan.com/a/mdev/ios/2015/0325/1677.html -(UIColor*)mostColor{ #if __IPHONE_OS_V ...
- 【LeetCode OJ】Longest Substring Without Repeating Characters
题目链接:https://leetcode.com/problems/longest-substring-without-repeating-characters/ 题目:Given a string ...
- 《转载》为什么我要在2018年学习Python?
本文转载自36kr 从网页编程到时髦的人工智能,机器学习,这个享有"瑞士军刀(万能工具)"盛誉的Python语言, 你学会了吗? 编者注: 根据维基百科的解释,"Pyth ...
- 微信小程序源码案例大全
微信小程序demo:足球,赛事分析 小程序简易导航 小程序demo:办公审批 小程序Demo:电魔方 小程序demo:借阅伴侣 微信小程序demo:投票 微信小程序demo:健康生活 小程序demo: ...
- C语言预处理命令详解
一 前言 预处理(或称预编译)是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作.预处理指令指示在程序正式编译前就由编译器进行的操作,可放在程序中任何位置. 预处理是C语言的一个重要功能 ...
- 【软件分析与挖掘】ELBlocker: Predicting blocking bugs with ensemble imbalance learning
摘要: 提出一种方法——ELBlocker,用于自动检测出Blocking Bugs(prevent other bugs from being fixed). 难度在于这些Blocking Bugs仅 ...
- openstack 中镜像状态详解 Image Statuses
Images in Glance can be in one the following statuses: queued The image identifier has been reserved ...
- 黄金票据(Golden Ticket)的原理与实践
0.黄金票据是什么? 在与认证过程中,经过client与AS的通信会得到TGT,带着TGT想TGS请求,得到票据ticket,用这个ticket可以来访问应用服务器.如果这段有什么疑问,欢迎参考Ker ...
- shell 中的$0 $1 $* $@ $# $$ $? $() $(())
$0: 脚本本身文件名称 : 命令行第一个参数,$2为第二个,以此类推 $*: 所有参数列表 $@: 所有参数列表 $#: 参数个数 $$: 脚本运行时的PID $?: 脚本退出码 ∗与@的区别 当命 ...