zoukankan      html  css  js  c++  java
  • SPOJ 1825 Free tour II 树分治

    题意:

    有N个顶点的树,节点间有权值, 节点分为黑点和白点。 找一条最长路径使得 路径上黑点数量不超过K个。

    题解:

    此题是qzc的论文里的题,没看懂qzc写的,后来看的别人的代码才理解了。

    先引用一下这位神犇的题解:http://hi.baidu.com/fuqbppvrgcbactd/item/14a81a1bdbd9f98888a956b9

    在以ROOT为根的树上,我们可以这样表示状态:F[ i , j ] 表示它的第 I 个子树中经过的不超过 J 个黑点的路径中,最长的一条的长度是多少,这样可以保证 F[ I , J ] 的递增性。要求出F[ I , J ] ,我们只要对所有子树进行一次DFS即可,复杂度是O( N )的。不过如果要保存这样的状态对于某些数据可能有些困难,因为数据范围太大了,我们可以通过以下方法来优化:我们要求的 F[ i , j ] 是把所有做过的子树全部保存起来,不过我们要用的只是对于每个 J 的最大值!所以我们可以根据这一点进行一个对空间的优化。把 F[ i , j ] 变成一维的 F[ i ] 表示当前已经计算过的子树中,经过黑点数不超过 i 个的路径中最长的长度是多少,对于当前所计算的子树,用 G[ i ] 表示当前子树中经过黑色点数严格为 i 个的路径中最长的路径的长度是多少。可以比较G[ i ] 和 F[ i ] 的大小来更新 F[ i ] ,依然可以保证 F[ i ] 的递增。

    不过每次更新一次 F[ i ] 的时候的复杂度是 F[ i ] 和 G[ i ] 深度的最大值,对于某些数据可能达到O( n^2 ) ,所以在进行更新之前对子树进行一次排序,关键字是该子树中路径经过最多数量的黑色节点的数量。

    我就是没有看懂怎么从n^2变成logn的,所以特别解释一下:

    因为最多只有n个黑色的节点,所以枚举黑色节点的个数进行更新即可,即对于下标相同的F数组,不用区分,直接合并取最大值。

     

    View Code
      1 #include <iostream>
      2 #include <cstring>
      3 #include <cstdio>
      4 #include <cstdlib>
      5 #include <algorithm>
      6 
      7 #define N 450010
      8 #define M 1100000
      9 #define INF 1e8
     10 
     11 using namespace std;
     12 
     13 int head[N],next[M],len[M],to[M];
     14 bool vis[N];
     15 int bk[N],sz[N],lim[N],num[N],dep[N],g[N],mg[N];
     16 int n,m,md,root,cnt,mn,ans;
     17 
     18 inline void init()
     19 {
     20     memset(head,-1,sizeof head); cnt=0;
     21     memset(bk,0,sizeof bk);
     22 }
     23 
     24 inline bool cmp(int x,int y)
     25 {
     26     return dep[to[x]]<dep[to[y]];
     27 }
     28 
     29 inline void add(int u,int v,int w)
     30 {
     31     to[cnt]=v; len[cnt]=w; next[cnt]=head[u]; head[u]=cnt++;
     32 }
     33 
     34 inline void read()
     35 {
     36     init(); 
     37     scanf("%d%d%d",&n,&md,&m);
     38     for(int i=1,a;i<=m;i++)
     39         scanf("%d",&a),bk[a]=1;
     40     for(int i=1,a,b,c;i<n;i++)
     41     {
     42         scanf("%d%d%d",&a,&b,&c);
     43         add(a,b,c); add(b,a,c);
     44     }
     45 }
     46 
     47 inline void getsize(int u,int fa)
     48 {
     49     sz[u]=1; lim[u]=0;
     50     for(int i=head[u];~i;i=next[i])
     51         if(to[i]!=fa&&!vis[to[i]])
     52         {
     53             getsize(to[i],u);
     54             sz[u]+=sz[to[i]];
     55             lim[u]=max(lim[u],sz[to[i]]);
     56         }
     57 }
     58 
     59 inline void getroot(int p,int u,int fa)
     60 {
     61     lim[u]=max(lim[u],sz[p]-sz[u]);
     62     if(lim[u]<mn) mn=lim[u],root=u;
     63     for(int i=head[u];~i;i=next[i])
     64         if(to[i]!=fa&&!vis[to[i]]) getroot(p,to[i],u);
     65 }
     66 
     67 inline void getdep(int u,int fa)
     68 {
     69     dep[u]=bk[u]; int res=0;
     70     for(int i=head[u];~i;i=next[i])
     71         if(to[i]!=fa&&!vis[to[i]])
     72         {
     73             getdep(to[i],u);
     74             res=max(res,dep[to[i]]);
     75         }
     76     dep[u]+=res;
     77 }
     78 
     79 inline void getg(int u,int fa,int d,int c)
     80 {
     81     g[c]=max(g[c],d);
     82     for(int i=head[u];~i;i=next[i])
     83         if(to[i]!=fa&&!vis[to[i]])
     84             getg(to[i],u,d+len[i],c+bk[to[i]]);
     85 }
     86 
     87 inline void getans(int u,int fa)
     88 {
     89     getsize(u,fa);
     90     mn=INF;
     91     getroot(u,u,fa);
     92     int rt=root,tot=0;
     93     vis[rt]=true;
     94     for(int i=head[rt];~i;i=next[i])
     95         if(!vis[to[i]]) getans(to[i],to[i]);
     96     for(int i=head[rt];~i;i=next[i])
     97         if(!vis[to[i]])
     98         {
     99             getdep(to[i],rt);
    100             num[++tot]=i;
    101         }
    102     sort(num+1,num+1+tot,cmp);
    103     for(int i=0;i<=dep[to[num[tot]]];i++) mg[i]=-INF;
    104     for(int i=1;i<=tot;i++)
    105     {
    106         int v=to[num[i]],d=dep[v];
    107         int val=len[num[i]];
    108         for(int j=0;j<=d;j++) g[j]=-INF;
    109         getg(v,rt,val,bk[v]);
    110         if(i!=1)
    111         {
    112             for(int j=0;j<=md-bk[rt]&&j<=d;j++)
    113             {
    114                 int sa=min(dep[to[num[i-1]]],md-bk[rt]-j);
    115                 if(mg[sa]==-INF) break;
    116                 if(g[j]!=-INF) ans=max(ans,mg[sa]+g[j]);
    117             }
    118         }
    119         for(int j=0;j<=d;j++)
    120         {
    121             mg[j]=max(g[j],mg[j]);
    122             if(j) mg[j]=max(mg[j],mg[j-1]);
    123             if(j+bk[rt]<=md) ans=max(ans,mg[j]);
    124         }
    125     }
    126     vis[rt]=false;
    127 }
    128 
    129 inline void go()
    130 {
    131     memset(vis,0,sizeof vis);
    132     getans(1,1); 
    133     printf("%d\n",ans);
    134 }
    135 
    136 int main()
    137 {
    138     read();
    139     go();
    140     return 0;
    141 }

    感觉树分治的细节挺多的。。。。囧。。。

  • 相关阅读:
    CentOS 6 + bochs-2.6 + gdb 调试 linux 0.11 —— 成功
    操作系统原理——互斥同步
    CentOS 6 bochs-2.6 gdb 调试 linux 0.11——bochsrc-fd1-gdb.bxrc
    vim+xxd=强大的十六进制编辑器
    【t043】成绩查询
    【u251】心灵的抚慰
    【t041】距离之和
    【t086】防护伞
    Java Web整合开发(37) -- SVN
    ubuntu命令
  • 原文地址:https://www.cnblogs.com/proverbs/p/2869128.html
Copyright © 2011-2022 走看看