点分治模板

POJ1741
在这里插入图片描述
Sample Input
5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0

Sample Output
8

点分治裸题。
点分治的主要思想就是:对这个题来讲的话
在这里插入图片描述
我们先随便找一个点 1.既然是分治。可以分为两种情况。
1.点对在1的不同子树内。
2.点对在1的相同子树x内。
同时情况2又是 像这样可以分为两种情况。
我们递归下去每次只计算情况1就好了。
如何计算:可以将以1为根的所有节点的 dep[ ] dfs处理出来。dep[ ]是路径而非深度。
cla(x,now)函数:将这些dep[ ]排序之后 用尺取法两个指针l,r分别从前后扫描。条件是dep[l]+dep[r]。。。我们这样会多计算一些东西。同一颗子树里面dep[u]+dep[v]<=K的点对。但我们这个函数本身计算的就是dep[x]+dep[y]<=K的点对。所以只需要再减去每个子树不合法的情况即可。也就是 cal(edge[i].to,egde[i].len)。看代码就好理解了。

证明可知,随便找着一个点记为root,当root为当前树的中心。时间复杂度较低。getroot(x,fa)函数用来找一个树的重心。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#define foru(i,a,b) for(int i=a;i<=b;++i)
#define m(a,b) memset(a,b,sizeof a)
#define en '\n'
using namespace std;
typedef long long ll;
const int N=1e4+5,M=1e5+5,INF=0x3f3f3f3f;
template<class T>void rd(T &x)
{
   
    x=0;int f=0;char ch=getchar();
    while(ch<'0'||ch>'9')  {
   f|=(ch=='-');ch=getchar();}
    while(ch>='0'&&ch<='9'){
   x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x=f?-x:x;
    return;
}
struct Edge{
   int to,len,nex;}edge[N<<1];
int head[N],tot;
void add(int from,int to,int len)
{
   
    edge[++tot]=(Edge){
   to,len,head[from]};head[from]=tot;
    edge[++tot]=(Edge){
   from,len,head[to]};head[to]=tot;
}
ll ans=0;
int root,sum;
int sz[N],mx[N],vis[N];//sz[i]以i为根的子树节点总个数 mx[i]是以i为根的几叉树最大的儿子节点数目 vis[]标记这个节点是否当过root
void getroot(int x,int fa)//查找树的重心
{
   
    sz[x]=1,mx[x]=0;
    for(int i=head[x];i;i=edge[i].nex)
    {
   
        int y=edge[i].to;
        if(vis[y]||y==fa) continue;
        getroot(y,x);
        sz[x]+=sz[y];
        mx[x]=max(mx[x],sz[y]);
    }
    mx[x]=max(mx[x],sum-sz[x]);//我觉着这个应该是x通往fa的那一条子树的大小.
    if(mx[x]<mx[root]) root=x;//更新root是谁
}
int d[N],dep[N];
void getd(int x,int fa)
{
   
    d[++d[0]]=dep[x];
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值