点分治Day1

树套树Day2暂且搁置...因为Day1的题我各种不会做...

唯一过了一道还是整体二分过的...

我们来一点愉快的算法,先不考虑数据结构这种骚东西了

毕竟还在发烧,就先码码这几天在搞的点分治吧

hx你又挖一个大坑赶紧去填树套树

点分治用于解决“树上路径点权统计问题”

...讲不太清楚,大家可以直接看题

点分治的思想呢,就是我们在树上走路径的时候,对于一个点,有两种方案:

1.选

2.不选

如果“不选”一个点,我们可以知道我们也会“不选”他的子树,递归处理即可

如果选一个点,有一个特别重要的性质:

如果一条路径要经过这个点,那么他必然是由两条在这个点不同子树中到这个点的路径组合而成

然后我们面临两条选择

1.直接dfs做

2.“动态点分治”(Orz popoqqq大爷)

由于是Day1...

我们来讲讲dfs!

我们知道,树这个东西是递归定义的,所以我们处理一棵树相当于处理它的根节点,然后递归处理它的每个子树

现在我们面临一个问题:根节点可以是任意的,我们怎么选根节点呢

考虑贪心的思想:让递归层数最小

我们每次选出的根节点要让这棵树的“总深度”最小

大概就是这样的

void getroot(int v,int fa)
{
    son[v] = 1; f[v] = 0;//f记录以v为根的最大子树的大小 
    for(int i = head[v];i;i=e[i].next)
        if(e[i].to != fa && !vis[e[i].to]) {
            getroot(e[i].to,v);//递归更新 
            son[v] += son[e[i].to];
            f[v] = max(f[v],son[e[i].to]);//比较每个子树 
        }
    f[v] = max(f[v],sum-son[v]);//别忘了以v父节点为根的子树 
    if(f[v] < f[root]) root = v;//更新当前根 
}
Getroot

理解了这个思想,我们就可以看一下下面的例题:

poj1741

给一颗n个节点的树,每条边上有一个距离v(v<=1000)。定义d(u,v)为u到v的最小距离。给定k值,求有多少点对(u,v)使u到v的距离小于等于k。数据范围:n<=10000,k<2^31

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<algorithm>
using namespace std;
#define INF 0x7fffffff
struct node{int y,v,next;}e[20010];
int n,len,k,root,sum,ans,Link[10010],f[10010],vis[10010],son[10010],d[10010],deep[10010];
inline int read()
{
    int x=0,f=1;  char ch=getchar();
    while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getchar();}
    while(isdigit(ch))  {x=x*10+ch-'0';  ch=getchar();}
    return x*f;
}
void insert(int x,int y,int v)
{
    e[++len].next=Link[x];
    Link[x]=len;
    e[len].v=v;
    e[len].y=y;
}
void getroot(int x,int fa)
{
    son[x]=1;  f[x]=0;
    for(int i=Link[x];i;i=e[i].next)
    {
        if(e[i].y==fa||vis[e[i].y])  continue;
        getroot(e[i].y,x);
        son[x]+=son[e[i].y];
        f[x]=max(f[x],son[e[i].y]);
    }
    f[x]=max(f[x],sum-son[x]);
    if(f[x]<f[root])  root=x;
}
void getdeep(int x,int fa)
{
    deep[++deep[0]]=d[x];
    for(int i=Link[x];i;i=e[i].next)
    {
        if(e[i].y==fa||vis[e[i].y])  continue;
        d[e[i].y]=d[x]+e[i].v;
        getdeep(e[i].y,x);
    }
}
int cal(int x,int v)
{
    d[x]=v;  deep[0]=0;
    getdeep(x,0);
    sort(deep+1,deep+deep[0]+1);
    int l=1,r=deep[0],sum=0;
    while(l<r)
    {
        if(deep[l]+deep[r]<=k)  {sum+=r-l;  l++;}
        else r--;
    }
    return sum;
}
void solve(int x)
{
    ans+=cal(x,0);//计算答案
    vis[x]=1;
    for(int i=Link[x];i;i=e[i].next)
    {
        if(vis[e[i].y])  continue;
        ans-=cal(e[i].y,e[i].v);//计算不符合题意的答案
        sum=son[e[i].y];
        root=0;
        getroot(e[i].y,0);
        solve(root);
    }
}
int main()
{
    freopen("cin.in","r",stdin);
    freopen("cout.out","w",stdout);
    while(1)
    {
        ans=0,root=0,len=0;
        memset(vis,0,sizeof(vis));
        memset(Link,0,sizeof(Link));
        n=read();  k=read();
        if(n==0&&k==0)  break;
        for(int i=1;i<=n-1;i++)
        {
            int x=read(),y=read(),v=read();
            insert(x,y,v);   insert(y,x,v);
        }
        f[0]=INF;  sum=n;
        getroot(1,0);
        solve(root);
        printf("%d
",ans);
    }
    return 0;
}
View Code

题解:对于每个点记录子树中出现的距离值,对于一棵树的距离值数组,把它排序求一次ans1,再对每棵子树分别求一个自己对自己的ans2,ans1-Σans2即为最后的ans。

例题:

bzoj2599

poj1741

bzoj2152

这回有完整代码了...

明天更树套树Day2

近期要更的:

树套树Day2

数据结构综合刷题Day1

FFT&NTT综合刷题(这个不知道要几天...)

矩阵练习.pdf(我真是日了...)

杜教筛刷题

莫比乌斯反演刷题

点分治Day2

CDQ分治&整体二分

原文地址:https://www.cnblogs.com/Kong-Ruo/p/8032704.html