zoukankan      html  css  js  c++  java
  • 图论:点分治

    点分治我感觉是图论树部分比较考验脑力的一种题目了

    POJ1741

    题意:给一棵边带权树,问两点之间的距离小于等于K的点对有多少个

    满足条件的点对有两种情况:两个点的路径横跨树根,两个点位于同一颗子树中

    对于根节点进行一次dfs,求出deep,并将其从小到大排序

    void getdeep(int x,int fa)
    {
        dep[++dep[0]]=d[x];
        for(int tmp=g[x];tmp;tmp=e[tmp].next)
        {
            if(e[tmp].t==fa||vis[e[tmp].t]) continue;
            d[e[tmp].t]=d[x]+e[tmp].v;
            getdeep(e[tmp].t,x);
        }
    }

    然后看一下calculate

    int cal(int x,int tmp)
    {
        d[x]=tmp;dep[0]=0;
        getdeep(x,0);
        sort(dep+1,dep+dep[0]+1);
        int t=0,l,r;
        for(l=1,r=dep[0];l<r;)
        {
            if(dep[l]+dep[r]<=k) {t+=r-l;l++;}
            else r--;
        }
        return t;
    } 

    如果我们已经知道了此时所有点到根的距离a[i]

    a[x] + a[y] <= k的(x, y)对数就是结果,这个可以通过排序之后O(n)的复杂度求出

    然后根据分治的思想,分别对所有的儿子求一遍即可

    void work(int x)  //点分治 
    {
        ans+=cal(x,0);
        vis[x]=1;
        for(int tmp=g[x];tmp;tmp=e[tmp].next)
        {
            if(vis[e[tmp].t]) continue;
            ans-=cal(e[tmp].t,e[tmp].v);
            sum=ch[e[tmp].t];
            root=0;
            getroot(e[tmp].t,root);
            work(root);
        }
    }

    但是这会出现重复的——当前情况下两个点位于一颗子树中,那么应该将其减掉

    显然这两个点是满足题意的,为什么减掉呢?因为在对子树进行求解的时候,会重新计算

    在进行分治时,为了避免树退化成一条链而导致时间复杂度变为O(N^2),每次都找树的重心

    这样,所有的子树规模就会变的很小了。时间复杂度O(Nlog^2N)

    void getroot(int x,int fa)
    {
        ch[x]=1;f[x]=0;
        for(int tmp=g[x];tmp;tmp=e[tmp].next)
        {
            if(e[tmp].t==fa||vis[e[tmp].t]) continue;
            getroot(e[tmp].t,x);
            ch[x]+=ch[e[tmp].t];
            f[x]=max(f[x],ch[e[tmp].t]);
        }
        f[x]=max(f[x],sum-ch[x]);
        if(f[x]<f[root]) root=x;
    }

    有了板子不能救命,还要大量刷大量刷才可以,点分治确实是一个要命的地方,上次的省赛的一道题就栽了

      1 #include<cstdio>
      2 #include<algorithm>
      3 #include<cstring>
      4 using namespace std;
      5 const int maxn=10005;
      6 const int maxm=20005;
      7 const int INF=0x7fffffff;
      8 int n,k,ans;
      9 int cnt,g[maxn];
     10 struct Edge{int t,next,v;}e[maxm];
     11 
     12 int root,sum;
     13 int ch[maxn],f[maxn],dep[maxn],d[maxn];
     14 bool vis[maxn];
     15 
     16 void addedge(int u,int v,int w)
     17 {
     18     cnt++;
     19     e[cnt].t=v;e[cnt].v=w;
     20     e[cnt].next=g[u];g[u]=cnt;
     21 }
     22 int read()
     23 {
     24     int x=0,f=1;char ch=getchar();
     25     while(ch<'0'||ch>'9') {if(ch=='0')f=-1;ch=getchar();}
     26     while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
     27     return x*f;
     28 }
     29 void getroot(int x,int fa)
     30 {
     31     ch[x]=1;f[x]=0;
     32     for(int tmp=g[x];tmp;tmp=e[tmp].next)
     33     {
     34         if(e[tmp].t==fa||vis[e[tmp].t]) continue;
     35         getroot(e[tmp].t,x);
     36         ch[x]+=ch[e[tmp].t];
     37         f[x]=max(f[x],ch[e[tmp].t]);
     38     }
     39     f[x]=max(f[x],sum-ch[x]);
     40     if(f[x]<f[root]) root=x;
     41 }
     42 void getdeep(int x,int fa)
     43 {
     44     dep[++dep[0]]=d[x];
     45     for(int tmp=g[x];tmp;tmp=e[tmp].next)
     46     {
     47         if(e[tmp].t==fa||vis[e[tmp].t]) continue;
     48         d[e[tmp].t]=d[x]+e[tmp].v;
     49         getdeep(e[tmp].t,x);
     50     }
     51 }
     52 int cal(int x,int tmp)
     53 {
     54     d[x]=tmp;dep[0]=0;
     55     getdeep(x,0);
     56     sort(dep+1,dep+dep[0]+1);
     57     int t=0,l,r;
     58     for(l=1,r=dep[0];l<r;)
     59     {
     60         if(dep[l]+dep[r]<=k) {t+=r-l;l++;}
     61         else r--;
     62     }
     63     return t;
     64 } 
     65 void work(int x)  //点分治 
     66 {
     67     ans+=cal(x,0);
     68     vis[x]=1;
     69     for(int tmp=g[x];tmp;tmp=e[tmp].next)
     70     {
     71         if(vis[e[tmp].t]) continue;
     72         ans-=cal(e[tmp].t,e[tmp].v);
     73         sum=ch[e[tmp].t];
     74         root=0;
     75         getroot(e[tmp].t,root);
     76         work(root);
     77     }
     78 }
     79 void init()
     80 {
     81     ans=root=cnt=0;
     82     memset(vis,0,sizeof(vis));
     83     memset(g,0,sizeof(g));
     84 }
     85 int main()
     86 {
     87     int x,y,z;
     88     while(1)
     89     {
     90         init();
     91         n=read();k=read();
     92         if(n==0) break;
     93         for(int i=1;i<n;i++)
     94         {
     95             x=read();y=read();z=read();
     96             addedge(x,y,z);
     97             addedge(y,x,z);
     98         }
     99         sum=n;
    100         f[0]=INF;
    101         getroot(1,0);
    102         work(root);
    103         printf("%d
    ",ans);
    104     }
    105 
    106     return 0;
    107 }
  • 相关阅读:
    vant框架的select下拉框
    监听滚轴高度
    关于使用iframe的父子页面进行简单的相互传值
    vue监听移动端物理返回
    vue+ElementUI项目中,input只能输入正整数的验证
    移动端公共样式
    协程嵌套协程
    基础知识
    汉化包
    .ui转.py文件命令
  • 原文地址:https://www.cnblogs.com/aininot260/p/9461358.html
Copyright © 2011-2022 走看看