zoukankan      html  css  js  c++  java
  • 点分治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分治&整体二分

  • 相关阅读:
    在编码转错的情况下,如何恢复
    【娱乐】给你的电脑检查兼容性,并获取你的电脑上安装的软件
    发布一个纯PHP的中文关键字自动提取工具
    [转]程序员能力矩阵 Programmer Competency Matrix
    解决PHP数组内存耗用太多的问题
    哈希表之数学原理
    PHP高级编程之单线程实现并行抓取网页
    如何自动的检测字符串编码
    如何检测网络中断, 并自动重启网卡
    完全二叉树判断,简单而复杂
  • 原文地址:https://www.cnblogs.com/Kong-Ruo/p/8032704.html
Copyright © 2011-2022 走看看