博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
点分治学习笔记
阅读量:4709 次
发布时间:2019-06-10

本文共 7648 字,大约阅读时间需要 25 分钟。

这里就不讲点分治的原理以及一些注意事项了,只讲几道例题吧(懒

 

给定一棵有n个点的树

 

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

 

一看标题就是点分治了嘛...

这是之前在机房打的

对每个路径长度进行标记即可,考虑到重复标记,容斥原理搞一下对于子树的答案贡献权值-1即可(就是更新的时候把新增贡献改为-1)

 

 

1 #include
2 #include
3 #include
4 #include
5 #define R register 6 using namespace std; 7 inline int read(){ 8 int ans=0,f=1;char chr=getchar(); 9 while(!isdigit(chr)){
if(chr=='-') f=-1;chr=getchar();}10 while(isdigit(chr)){ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();}11 return ans*f;12 }const int M=100005<<1;13 int head[M],ver[M],nxt[M],val[M],tot,n,m,x,root,sz[M],mx[M],vis[M],ANS[M],p,a[M],dis[M],s;14 inline void add(int x,int y,int z){ver[++tot]=y;val[tot]=z;nxt[tot]=head[x];head[x]=tot;}15 void Find_Gf(int x,int fa){
//找根16 sz[x]=1;mx[x]=0;17 for(R int i=head[x];i;i=nxt[i])18 if(ver[i]!=fa&&!vis[ver[i]])19 Find_Gf(ver[i],x),sz[x]+=sz[ver[i]],mx[x]=max(mx[x],sz[ver[i]]);20 mx[x]=max(s-sz[x],mx[x]);21 if(mx[x]

 

 

 

给定一棵树,求树上路径是3的倍数的条数

 

看到树上路径联想到点分

 

显然我们不可能开一个桶存边...观察发现只要记录下边权对3取模的结果就好了

并且对一个点来说,它的子树的贡献是这样的:

$ANS=cnt[1]*cnt[2]*2+{cnt[0]}^2$

考虑排除重复计算,在处理子树的子树时减掉即可,模板题

总数显然就是树上随便两个点并且可以重复所以就是${n}^2$

关于答案...除一个gcd就好了

1 #include
2 #include
3 #include
4 #include
5 #define int long long 6 using namespace std; 7 const int M=100005<<1; 8 int head[M],ver[M],nxt[M],tot,val[M],n,m,s,vis[M],sz[M],mx[M],root,ans,cnt[M]; 9 inline void add(int x,int y,int z){ver[++tot]=y;nxt[tot]=head[x];head[x]=tot;val[tot]=z;}10 inline int read(){11 int ans=0,f=1;char chr=getchar();12 while(!isdigit(chr)){chr=getchar();}13 while(isdigit(chr)){ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();}14 return ans;15 }16 inline void cmax(int &x,int y){
if(x
mx[x]) root=x;26 }27 void dfs(int x,int fa,int dis){28 ++cnt[dis%3];29 for(int i=head[x];i;i=nxt[i]){30 if(ver[i]==fa||vis[ver[i]]) continue;31 dfs(ver[i],x,(dis+val[i]%3));32 }33 }34 int Solve(int x,int w){35 cnt[0]=cnt[1]=cnt[2]=0;36 dfs(x,0,w);37 return cnt[1]*cnt[2]*2+cnt[0]*cnt[0];38 }39 void Divide(int x){40 ans+=Solve(x,0);41 vis[x]=1;42 for(int i=head[x];i;i=nxt[i]){43 if(vis[ver[i]]) continue;44 ans-=Solve(ver[i],val[i]);45 root=0,s=sz[ver[i]];mx[ver[i]]=1;46 find_gf(ver[i],0);Divide(root);47 }48 }49 signed main(){50 n=read();51 for(int i=1,x,y,z;i

 

求一棵树上边权值和为k的条数

显然淀粉质无疑了...

先考虑权值小于等于K的条数,减去小于等于K-1的条数即可

在计算贡献的时候我们显然不可能双重循环寻找答案,时间复杂度会退化成O(${n}^2$$logn$),这样不如暴力+LCA求解方便快捷

其实这里面是含有一个单调性的

在点分的dfs过程中记录点到root的距离(用一个数组存储),然后sort这个数组,O(n)扫描即可

时间复杂度O(n${log}^2$n),在可以承受的范围内(还有一些人直接把其中一个logn当常数来看了非严格来讲O($nlogn$)也不算错,毕竟logn最大也只有20不到)

这题还有一个双倍经验()

1 #include
2 #include
3 #include
4 #include
5 #define int long long 6 using namespace std; 7 const int M=100005<<1; 8 int head[M],ver[M],nxt[M],tot,val[M],n,m,s,vis[M],sz[M],mx[M],root,ans,a[M]; 9 inline void add(int x,int y,int z){ver[++tot]=y;nxt[tot]=head[x];head[x]=tot;val[tot]=z;}10 inline int read(){11 int ans=0,f=1;char chr=getchar();12 while(!isdigit(chr)){chr=getchar();}13 while(isdigit(chr)){ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();}14 return ans;15 }16 inline void cmax(int &x,int y){
if(x
mx[x]) root=x;26 }27 int p;28 void dfs(int x,int fa,int dis){29 a[++p]=dis;30 for(int i=head[x];i;i=nxt[i]){31 if(ver[i]==fa||vis[ver[i]]) continue;32 dfs(ver[i],x,dis+1);33 }34 }35 int Solve(int x,int w){36 p=0;dfs(x,0,w);37 sort(a+1,a+p+1);38 int l=1,r=p,ans=0; 39 while(l

 

 

 

  求一棵树上权值为K且覆盖边数最少的路径
  就是把上面的问题结合一下,然后观察到K不是很大...
  注意一点当前边权大于K的时候及时退出...不然桶会炸
  
1 #include
2 #include
3 #include
4 #include
5 #include
6 using namespace std; 7 inline int read(){ 8 int ans=0,f=1;char chr=getchar(); 9 while(!isdigit(chr)){
if(chr=='-') f=-1;chr=getchar();}10 while(isdigit(chr)){ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();}11 return ans*f;12 }const int M=200005;13 int n,kk,head[M<<1],ver[M<<1],nxt[M<<1],val[M<<1],tot,mx[M],vis[M],sz[M],root,s,Ans,dep[M],dis[M],p,b[1000100];14 inline void add(int x,int y,int z){ver[++tot]=y;val[tot]=z;nxt[tot]=head[x];head[x]=tot;}15 void Find_GF(int x,int fa){16 sz[x]=1,mx[x]=0;17 for(register int i=head[x];i;i=nxt[i]){18 if(vis[ver[i]]||ver[i]==fa) continue;19 Find_GF(ver[i],x);20 sz[x]+=sz[ver[i]],mx[x]=max(mx[x],sz[ver[i]]);21 }mx[x]=max(mx[x],s-sz[x]);22 if(mx[x]
kk) return;26 dis[++p]=d1;dep[p]=d2;27 for(register int i=head[x];i;i=nxt[i]){28 if(ver[i]==fa || vis[ver[i]]) continue;29 DFS(ver[i],x,d1+val[i],d2+1);30 }31 }32 inline void Calc(int x){33 b[0]=0,p=0;34 for(register int i=head[x];i;i=nxt[i]){35 if(vis[ver[i]]) continue;36 int lst=p;37 DFS(ver[i],x,val[i],1);38 for(register int j=lst+1;j<=p;++j)39 Ans=min(Ans,b[kk-dis[j]]+dep[j]);40 for(register int j=lst+1;j<=p;++j)41 b[dis[j]]=min(b[dis[j]],dep[j]);42 }for(int i=1;i<=p;i++) b[dis[i]]=1e9;43 }44 void Divide(int x){45 vis[x]=1,Calc(x);46 for(register int i=head[x];i;i=nxt[i]){47 if(vis[ver[i]]) continue;48 root=0,s=sz[ver[i]];root=0;49 Find_GF(ver[i],x);50 Divide(root);51 }52 } 53 int main(){Ans=0x3f3f3f3f;54 n=read(),kk=read();55 for(register int x,y,z,i=1;i
=n)puts("-1");62 else printf("%d",Ans);63 return 0;64 }

 

 
有一些难一点的覆盖问题以后可能会贴上来,但是博主tcl...有可能会咕
[UPD4.09]
然而并没有咕...不过是最后一次更新这篇啦
 

  题目大意:

    给定一棵n个节点的树和m个点对,在树上求一点使这个点到一个点对中每个点的距离之和的最大值的最小值...

 

  见guai了...一眼看到根本没想到是点分治(然鹅打起来也不太像点分治...)看了一些大佬的题解才知道

  我们随便找一个点比如重心,以其为根节点的儿子节点向外扩展(DFS),记录到每个节点的距离dis[]、它的祖先(根节点的某一个儿子)rt[]...

  然后每个点对更新一下maxn值...并且记录那些点对可以取到maxn值...显然由于我们在模拟点分的过程...如果两个满足条件的点对却不连在同一个祖先(rt[])上也是要舍掉的...

  注意这里的DFS过程中不像其他点分治过程一样不能访问已经vis[]过的点,因为在遍历的过程中是要记录每一个节点在当前情况下的距离的!!

  如果在某一个情况下访问到了已经访问过的节点只能说明所有情况都访问过了...可以直接输出退出了

 

1 #include
2 #include
3 #include
4 #include
5 #define R register 6 #define GO(i) for(R int i=head[x];i;i=nxt[i]) 7 using namespace std; 8 inline int read(){ 9 int ans=0,f=1;char chr=getchar();10 while(!isdigit(chr)){
if(chr=='-') f=-1;chr=getchar();}11 while(isdigit(chr)){ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();}12 return ans*f;13 }const int M=1e5+5;14 int head[M<<1],ver[M<<1],nxt[M<<1],val[M<<1],tot,n,m,p[M],sz[M],mx[M],vis[M],s,root,px[M],py[M],rt[M],dis[M],Ans;15 inline void add(int x,int y,int z){ver[++tot]=y;val[tot]=z;nxt[tot]=head[x];head[x]=tot;}16 inline void cmax(int &x,int y){
if(x
y) x=y;}18 void Find_GF(int x,int fa){19 sz[x]=1,mx[x]=0;20 GO(i){21 if(ver[i]==fa||vis[ver[i]]) continue;22 Find_GF(ver[i],x);23 sz[x]+=sz[ver[i]],cmax(mx[x],sz[ver[i]]);24 }cmax(mx[x],s-sz[x]);25 if(mx[root]>mx[x]) root=x;26 }27 void DFS(int x,int fa,int root){28 rt[x]=root;29 GO(i){30 if(ver[i]==fa) continue;31 dis[ver[i]]=dis[x]+val[i];32 DFS(ver[i],x,root);33 }34 }35 void Divide(int x){36 if(vis[x]){cout<
maxn)42 maxn=dis[px[i]]+dis[py[i]],p[siz=1]=i;43 else if(dis[px[i]]+dis[py[i]]==maxn) p[++siz]=i;44 cmin(Ans,maxn);45 for(R int i=1;i<=siz;++i){46 if(rt[px[p[i]]]!=rt[py[p[i]]]){cout<

 

 

 

  

  

 
 
 
 

 

转载于:https://www.cnblogs.com/zhenglw/p/10658210.html

你可能感兴趣的文章
spotlight工具监控oracle
查看>>
数据集成工具:Teiid实践
查看>>
寒假集训【1.28】
查看>>
Tengine编译安装config项目清单
查看>>
产品经理聊产品--mac book pro 2018 初体验
查看>>
可以进行SHA-1,SHA-224,SHA-256,SHA-384,SHA-512五种算法签名的工具类,以及简单说明
查看>>
c#进程间通讯方案之IPC通道
查看>>
CSS3分享按钮动画特效
查看>>
[BT5]信息收集1-1 Dnsenum
查看>>
tf.reduce_mean
查看>>
IDA相关下载
查看>>
[Codeforces] #441 div.2
查看>>
BusHelper
查看>>
数据整合构思
查看>>
pandas所占内存释放
查看>>
MySQL关于TYPE和ENGIN的一点问题
查看>>
工作中的一些问题总结
查看>>
母猪的产后护理——一些零碎的知识
查看>>
你所不知的 CSS ::before 和 ::after 伪元素用法
查看>>
POJ 3666 Making the Grade
查看>>