洛谷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有一条长度 ...
随机推荐
- 文件完整性hash验证demo(python脚本)
一个简单的文件完整性hash验证脚本 #!/usr/bin/env python # -*- coding: utf- -*- import os import hashlib import json ...
- ASP代码审计学习笔记 -4.命令执行漏洞
命令执行漏洞: 保存为cmd.asp,提交链接: http://localhost/cmd.asp?ip=127.0.0.1 即可执行命令 <%ip=request("ip" ...
- 【RF库Collections测试】lists should be equal
场景一:msg=None 场景二:自定义msg 场景三:自定义msg和values,且values为布尔类型False或者字符串False和No Values 场景四:自定义msg和values,且v ...
- SaltStack 批量执行脚本
这里演示如何使用 salt-master 对多台 salt-minion 批量执行脚本,步骤如下: [root@localhost ~]$ cat /srv/salt/top.sls # 先定义入口配 ...
- Linux命令之乐--iconv
用法: 实际应用: 批量转换文件编码: [root@wls12c PCK]$ for tfile in `ls -l|awk '{print $9}'`;do echo "iconv -f ...
- AddParent
using UnityEngine; using UnityEditor; using System.Collections; public class AddParent : ScriptableO ...
- PyQt4程序图标
程序图标就是一个小图片,通常显示在程序图标的左上角(ubuntu gnome在最上侧). #!/usr/bin/python # -*- coding:utf-8 -*- import sys fro ...
- android R文件不能识别?
android R文件引入不了原因可能是: 1.xml有错误,导致R文件生成失败:(修改xml,并clear,然后再重新Bulid一下即可) 2.如果是图片,可能是命名有问题,查看并修改(不要以数字开 ...
- Bat脚本实现监控进程功能
脚本不间断监控notepad.exe进程是否执行,若停止,则自动重启该进程,程序如下: @echo off set _task = notepad.exe set _svr = c:\windows\ ...
- sencha touch carousel 扩展 CardList 可绑定data/store
扩展代码: /* *扩展carousel *通过data,tpl,store配置数据 */ Ext.define('ux.CardList', { extend: 'Ext.carousel.Caro ...