首页 > 其他分享 >[学习笔记]点分治

[学习笔记]点分治

时间:2024-06-03 21:44:50浏览次数:10  
标签:int siz sum 分治 笔记 学习 num nowmax now

一、主要思想

很容易理解,我们将一个树以一个节点分割成若干个子树。对于这个节点,我们以一些方式统计和改变答案,然后不断地向子树递归。

那应该选择哪个节点呢?显然是重心。树的重心有一个性质:所有子树的大小小于等于当前树的大小的二分之一。也就是说,这保证了递归层数 \(log_2\) 的时间复杂度。

二、实现

#include<bits/stdc++.h>
using namespace std;
const int N=40010;
int n,cnt,first[N],k,root,p,ans;
int siz[N],rootsiz,dis[N];
bool vis[N];
struct edeg
{
	int to,v,next;
}e[N*2];
void addedge(int x,int y,int z)
{
	e[++cnt].to=y;
	e[cnt].v=z;
	e[cnt].next=first[x];
	first[x]=cnt;
}
void findroot(int now,int fa,int all)
{
	int nowmax=0;
	siz[now]=1;
	for(int i=first[now];i;i=e[i].next)
	{
		int to=e[i].to;
		if(to==fa||vis[to])continue;
		findroot(to,now,all);
		siz[now]+=siz[to];
		if(siz[to]>nowmax)
			nowmax=siz[to];
	}
	if(all-siz[now]>nowmax)
		nowmax=all-siz[now];
	if(nowmax<rootsiz)
	{
		root=now;
		rootsiz=nowmax;
	}
}
void getdis(int now,int fa,int dv)
{
	siz[now]=1;
	dis[++p]=dv;
	for(int i=first[now];i;i=e[i].next)
	{
		int to=e[i].to;
		if(to==fa||vis[to])continue;
		getdis(to,now,dv+e[i].v);
		siz[now]+=siz[to];
	}
}
void cc(int x)//计算答案
{
	sort(dis+1,dis+p+1);
	for(int i=p;i>=1;i--)
	{
		int r=upper_bound(dis+1,dis+i,k-dis[i])-(dis+1);
		ans+=r*x;
	}
}
void calc(int now,int fa,int all)
{
	rootsiz=100000;
	findroot(now,fa,all);
	p=0;
	vis[now]=1;
	getdis(root,0,0);
	cc(1);
    //此处使用容斥原理。先算所有答案,再减去同一颗子树内的答案,也可以用树形背包之类的。
	for(int i=first[root];i;i=e[i].next)
	{
		int to=e[i].to;
		if(to==fa||vis[to])continue;
		p=0;
		getdis(to,root,e[i].v);//计算距离
		cc(-1);
	}
	for(int i=first[root];i;i=e[i].next)
	{
		int to=e[i].to;
		if(to==fa||vis[to])continue;
		calc(to,root,all);
	}
}
int main()
{
	cin>>n;
	for(int i=1;i<n;i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		addedge(x,y,z);
		addedge(y,x,z);
	}
	cin>>k;
	calc(1,0,n);
	cout<<ans;
	return 0;
}

这是求树上点对有几个小于等于 \(k\) 的。

注意,只要可以通过一些方式计算当前点贡献的答案的,几乎都可以用点分治。

三、例题选讲

1. \(\color{purple}{P3714 [BJOI2017] 树的难题}\)

毒瘤题,希望再也不要遇到了。

首先肯定点分治,难点是怎么计算答案。应该把这条边所连的所有边按颜色排序,然后一边跑一边用两颗线段树维护,一个同色,一个异色,问题解决了。

#include<algorithm>
#include<list>
#include<vector>
#include<iostream>
#pragma GCC optimize("Ofast","O2")
using namespace std;
const int N=200010,inf=2000000009;
int n,m,sum[N<<2][2],tag[N<<2][2];
int siz[N],dis[N],dis2[N],rootsiz=N;
int root,p,ans=-inf,le,ri,c[N];
bool vis[N];
int read()
{
	int x,c,op=1;
	while(c=getchar(),c<'0'||c>'9')
		if(c=='-')op=-op;
	x=c^48;
	while(c=getchar(),c>='0'&&c<='9')
    	x=(x<<3)+(x<<1)+(c^48);
	return x*op;
}
struct dot
{
	int v,col;
};
vector<dot> e[N];
bool cmp(dot a,dot b)
{
	return a.col<b.col;
} 
void build(int p,int l,int r,int num)
{
    tag[p][num]=0;
    if(l==r){sum[p][num]=-inf;return ;}
    int mid=(l+r)>>1;
    build(p*2,l,mid,num);
    build(p*2+1,mid+1,r,num);
    sum[p][num]=-inf;
} 
void f(int p,int l,int r,int k,int num)
{
    tag[p][num]=k;
    sum[p][num]=k;
}
void push_down(int p,int l,int r,int num)
{
    int mid=(l+r)>>1;
    if(tag[p][num]==0)return;
    f(p*2,l,mid,tag[p][num],num);
    f(p*2+1,mid+1,r,tag[p][num],num);
    tag[p][num]=0;
}
void update(int nl,int nr,int l,int r,int p,int k,int num)
{
    if(nl<=l&&r<=nr)
    {
        sum[p][num]=max(sum[p][num],k);
        return ;
    }
    push_down(p,l,r,num);
    int mid=(l+r)>>1;
    if(nl<=mid)update(nl,nr,l,mid,p*2,k,num);
    if(nr>mid) update(nl,nr,mid+1,r,p*2+1,k,num);
    sum[p][num]=max(sum[p*2][num],sum[p*2+1][num]);
}
void clean(int nl,int nr,int l,int r,int p,int k,int num)
{
    if(nl<=l&&r<=nr)
    {
        sum[p][num]=-inf;
        tag[p][num]=-inf;
        return ;
    }
    push_down(p,l,r,num);
    int mid=(l+r)>>1;
    if(nl<=mid)update(nl,nr,l,mid,p*2,k,num);
    if(nr>mid) update(nl,nr,mid+1,r,p*2+1,k,num);
    sum[p][num]=max(sum[p*2][num],sum[p*2+1][num]);
}
int query(int x,int y,int l,int r,int p,int num)
{
	if(x==0)x=1;
	if(y==0)return -inf;
    int res=-inf;
    if(x<=l&&r<=y)return sum[p][num];
    int mid=(l+r)>>1;
    push_down(p,l,r,num);
    if(x<=mid)res=max(res,query(x,y,l,mid,p*2,num));
    if(y>mid)res=max(res,query(x,y,mid+1,r,p*2+1,num));
    return res;
}
void findroot(int now,int fa,int all)
{
	int nowmax=0;
	siz[now]=1;
	for(int i=0;i<e[now].size();i++)
	{
		int to=e[now][i].v;
		if(to==fa||vis[to])continue;
		findroot(to,now,all);
		siz[now]+=siz[to];
		if(siz[to]>nowmax)
			nowmax=siz[to];
	}
	if(all-siz[now]>nowmax)
		nowmax=all-siz[now];
	if(nowmax<rootsiz)
	{
		root=now;
		rootsiz=nowmax;
	}
}
void getdis(int now,int fa,int dv,int dl,int lastcol)
{
	siz[now]=1;
	dis[++p]=dv;
	dis2[p]=dl;
	for(int i=0;i<e[now].size();i++)
	{
		int to=e[now][i].v;
		int nowcol=e[now][i].col;
		if(to==fa||vis[to])continue;
		if(e[now][i].col==lastcol)
			getdis(to,now,dv,dl+1,nowcol);
		else getdis(to,now,dv+c[nowcol],dl+1,nowcol);
		siz[now]+=siz[to];
	}
}
void calc(int now,int fa,int all)
{
	rootsiz=N;
	findroot(now,fa,all);
	p=0;
	int l=0,lcol=0,top=1;
	dis[1]=0,dis2[1]=0;
	vis[root]=1;
	for(int i=0;i<e[root].size();i++)
	{
		int to=e[root][i].v;
		if(to==fa||vis[to])continue;
		getdis(to,root,c[e[root][i].col],1,e[root][i].col);
		if(e[root][i].col==lcol)
		{
			for(int j=l+1;j<=p;j++)
			{
				int lask,rask;
				if(dis2[j]<le)
					lask=le-dis2[j],rask=ri-dis2[j];
				else if(dis2[j]>=le&&dis2[j]<=ri)
					lask=0,rask=ri-dis2[j],ans=max(ans,dis[j]);
				else lask=0,rask=0;
				int t1=query(lask+1,rask+1,1,n+1,1,0),t2=query(lask+1,rask+1,1,n+1,1,1);
				if(t1!=-inf)ans=max(ans,t1-c[e[root][i].col]+dis[j]);
				if(t2!=-inf)ans=max(ans,t2+dis[j]);
			}
			for(int j=l+1;j<=p;j++)
			{
				update(dis2[j]+1,dis2[j]+1,1,n+1,1,dis[j],0);
			} 
		}
		else
		{
			for(int j=top;j<=l;j++)
				update(dis2[j]+1,dis2[j]+1,1,n+1,1,dis[j],1); 
			for(int j=l+1;j<=p;j++)
			{
				int lask,rask;
				if(dis2[j]<le)
					lask=le-dis2[j],rask=ri-dis2[j];
				else if(dis2[j]>=le&&dis2[j]<=ri)
					lask=0,rask=ri-dis2[j],ans=max(ans,dis[j]);
				else lask=0,rask=0;
				int t1=query(lask+1,rask+1,1,n+1,1,1);
				if(t1!=-inf)ans=max(ans,t1+dis[j]);
			}
			
			clean(1,n+1,1,n+1,1,-inf,0);
			for(int j=l+1;j<=p;j++)
				update(dis2[j]+1,dis2[j]+1,1,n+1,1,dis[j],0);
			
			top=l+1;
		}
		lcol=e[root][i].col;
		l=p;
	}
	clean(1,n+1,1,n+1,1,-inf,1);
	clean(1,n+1,1,n+1,1,-inf,0);
	int tysroot=root;
	for(int i=0;i<e[tysroot].size();i++)
	{
		int to=e[tysroot][i].v;
		if(to==fa||vis[to])continue;
		calc(to,tysroot,siz[to]);
	}
}
signed main()
{
	n=read();
	m=read();
	le=read();
	ri=read();
	for(int i=1;i<=m;i++)
		c[i]=read();
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read(),z=read();
		dot ax,bx;
		ax.v=y,ax.col=z;
		bx.v=x,bx.col=z;
		e[x].push_back(ax);
		e[y].push_back(bx);
	} 
	build(1,1,n+1,0);
	build(1,1,n+1,1); 
	for(int i=1;i<=n;i++)
	{
		stable_sort(e[i].begin(),e[i].end(),cmp);
	}
	calc(1,0,n);
	printf("%d",ans);
    return 0;
}

标签:int,siz,sum,分治,笔记,学习,num,nowmax,now
From: https://www.cnblogs.com/WindChime/p/18229718

相关文章

  • Git 笔记
    Git笔记git原理git的四个区域文件的四种状态git的工作流程安装git配置信息和获取帮助常用命令创建仓库跟踪文件gitadd取消跟踪gitrm提交到仓库gitcommit推送到远程分支gitpushcommit的查看、修改、合并搭建git服务器git原理git的四个区域工作......
  • g++编译过程学习笔记
    g++编译过程学习笔记学习用例使用很简单的多文件编译项目,进行编译过程的学习,主要文件构成如下:.├──include│└──hello.h└──src├──hello.cpp└──main.cpp其中hello.h声明了一个可以输出HelloWorld!的函数并在hello.cpp中完成实现。ma......
  • JAVA学习笔记6
    学习目标:精通JAVA学习内容:1.方法调用packagecn.itcast.day04.demo02;/*publicclassDemo01Method{publicstaticvoidmain(String[]args){for(intj=1;j<5;j++){for(inti=1;i<20;i++){System.out.print(“*”);}System.out.println();}}}......
  • system函数学习
    windows下system()函数详解windows操作系统下system()函数详解(主要是在C语言中的应用)函数名:system功能:发出一个DOS命令用法:intsystem(char*command);system函数已经被收录在标准c库中,可以直接调用程序例:include<stdlib.h>include<stdio.h>i......
  • Prism 学习之一
    1引用Prism.DryIOC2xmlns:prism="http://prismlibrary.com/"Application改成prism:PrismApplication3Windowsxmal中增加prism:ViewModelLocator.AutoWireViewModel="True"4文件夹ViewsViewModels记录一次理解Xmal<Grid><Text......
  • Markdown 学习
    Markdown标题1~6级一级标题二级标题三级标题四级标题五级标题六级标题Markdown字体字体效果粗体字体效果斜体字体效果斜体加粗字体效果删除线Markdown引用世上无难事,只怕有心人Markdown分割线Markdown图片本地路径网络路径Markdown超链接点击......
  • JSTL学习
    JSTL学习日记jstl相当于c++上的stl,当然不是说用法,只是意义上有很大的相似之处//开始学习//第一步,先下载并导入jstl的核心库(通过<@%uri="路径"去导入)<%@pagecontentType="text/html;charset=UTF-8"language="java"%><%--通过taglib标签引入所需要的库--%......
  • COD读书笔记
    计算机组成与设计课程复习与CSAPP中类似的部分做了忽略或者简化性能的度量知识回顾对于某个计算机X,定义性能和执行时间的关系表达式:\[\text{性能}_X=\frac{1}{\text{执行时间}_X}\]描述时钟周期和时钟频率的关系:\[\text{时钟周期}=\frac{1}{\text{时钟频率}}\]对......
  • 概率论笔记(上)
    学习视频如下:主要学习视频:《概率论与数理统计》教学视频全集(宋浩)_哔哩哔哩_bilibili其余知识点补充: 二维连续型随机变量的积分计算_哔哩哔哩_bilibili 014二维连续型随机变量_哔哩哔哩_bilibili 矩估计&最大似然估计通俗易懂版解释(自用)_哔哩哔哩_bilibili ......
  • 实战整体布局学习上
    1.```htmlindex1headermainfooter```2.```htmlindex1-页眉与页脚<!--中:搜索框--><divclass="search"><divclass="logo">JD</div><divclass="zoomiconfonticon-x......