zoukankan      html  css  js  c++  java
  • bzoj 4016 [FJOI2014]最短路径树问题(最短路径树+树分治)

    4016: [FJOI2014]最短路径树问题

    Time Limit: 5 Sec  Memory Limit: 512 MB
    Submit: 426  Solved: 147
    [Submit][Status][Discuss]

    Description

    给一个包含n个点,m条边的无向连通图。从顶点1出发,往其余所有点分别走一次并返回。
    往某一个点走时,选择总长度最短的路径走。若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径A为1,32,11,路 径B为1,3,2,11,路径B字典序较小。注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小)。到达该点后按原路返回,然后往其他 点走,直到所有点都走过。
    可以知道,经过的边会构成一棵最短路径树。请问,在这棵最短路径树上,最长的包含K个点的简单路径长度为多长?长度为该最长长度的不同路径有多少条?
    这里的简单路径是指:对于一个点最多只经过一次的路径。不同路径是指路径两端端点至少有一个不同,点A到点B的路径和点B到点A视为同一条路径。

    Input

    第一行输入三个正整数n,m,K,表示有 n个点m条边,要求的路径需要经过K个点。接下来输入m行,每行三个正整数Ai,Bi,Ci(1<=Ai,Bi<=n,1<=Ci& lt;=10000),表示Ai和Bi间有一条长度为Ci的边。数据保证输入的是连通的无向图。
     
     

    Output

    输出一行两个整数,以一个空格隔开,第一个整数表示包含K个点的路径最长为多长,第二个整数表示这样的不同的最长路径有多少条。
     

    Sample Input

    6 6 4
    1 2 1
    2 3 1
    3 4 1
    2 5 1
    3 6 1
    5 6 1

    Sample Output

    3 4

    HINT

    对于所有数据n<=30000,m<=60000,2<=K<=n。数据保证最短路径树上至少存在一条长度为K的路径。

    【思路】

           先求出满足要求的最短路树来。

           分治:求出过根节点的点对数,其它递归处理。

           求过根节点的点对:假设现在处理根节点的S子树,用f[i][0]表示前S-1棵子树中与根相距i个节点(不含根)的最长路径,f[i][1]表示方案数,类似的定义tmp为当前S子树的统计结果。一遍bfs构造出tmp,枚举该子树上的结点数更新答案,然后用tmp更新f。

           需要注意的有:

                  累计答案的诸多小细节。

                  f[][],tmp[][],queue的清零。

    【代码】

      1 #include<cstdio>
      2 #include<vector>
      3 #include<queue>
      4 #include<cstring>
      5 #include<iostream>
      6 #include<algorithm>
      7 using namespace std;
      8 
      9 const int N = 30000+10;
     10 const int INF = 1e9+1e9;
     11 
     12 struct Edge {
     13     int v,w;
     14     Edge(int v=0,int w=0):v(v),w(w) {}
     15     bool operator < (const Edge& rhs) const  {
     16         return v<rhs.v;
     17     }
     18 };
     19 queue<int> q;
     20 int n,m,K,root,size,d[N],inq[N];
     21 int ans1,ans2,siz[N],mx[N],vis[N],dep[N],dis[N],fa[N];
     22 vector<Edge> g[N],G[N];
     23 
     24 void spfa() {
     25     for(int i=2;i<=n;i++) d[i]=INF;
     26     memset(inq,0,sizeof(inq));
     27     inq[1]=1; q.push(1);
     28     while(!q.empty()) {
     29         int u=q.front(); q.pop(); inq[u]=0;
     30         for(int i=0;i<g[u].size();i++) {
     31             int v=g[u][i].v,w=g[u][i].w;
     32             if(d[v]>d[u]+w) {
     33                 d[v]=d[u]+w;
     34                 if(!inq[v]) inq[v]=1,q.push(v);
     35             }
     36         }
     37     }
     38 }
     39 void dfs(int u) {
     40     vis[u]=1;
     41     for(int i=0;i<g[u].size();i++) {
     42         int v=g[u][i].v,w=g[u][i].w;
     43         if(!vis[v] && d[v]==d[u]+w) {
     44             G[u].push_back(Edge(v,w));
     45             G[v].push_back(Edge(u,w));
     46             dfs(v);
     47         }
     48     }
     49 }
     50 void getroot(int u) {
     51     siz[u]=1; mx[u]=0;
     52     for(int i=0;i<G[u].size();i++) {
     53         int v=G[u][i].v;
     54         if(v!=fa[u] && !vis[v]) {
     55             fa[v]=u;
     56             getroot(v);
     57             siz[u]+=siz[v];
     58             if(siz[v]>mx[u]) mx[u]=siz[v];
     59         }
     60     }
     61     mx[u]=max(mx[u],size-siz[u]);
     62     if(mx[u]<mx[root]) root=u;
     63 }
     64 int f[N][2],tmp[N][2];
     65 void solve(int u,int S){
     66     vis[u]=1; f[0][1]=1;
     67     int m=G[u].size();
     68     for(int i=0;i<m;i++) {
     69         int v=G[u][i].v;
     70         if(!vis[v]) {
     71             while(!q.empty()) q.pop();                            //clear
     72              q.push(v),dep[v]=1,dis[v]=G[u][i].w,fa[v]=u;
     73             while(!q.empty()) {
     74                 int now=q.front(); q.pop();
     75                 int k=dep[now];
     76                 if(k>K) break;
     77                 if(dis[now]>tmp[k][0])
     78                     tmp[k][0]=dis[now],tmp[k][1]=0;
     79                 if(dis[now]==tmp[k][0]) tmp[k][1]++;
     80                 for(int j=0;j<G[now].size();j++) {
     81                     int to=G[now][j].v;
     82                     if(!vis[to] && to!=fa[now]) {
     83                         fa[to]=now;
     84                         dep[to]=dep[now]+1;
     85                         dis[to]=dis[now]+G[now][j].w;
     86                         q.push(to);
     87                     }
     88                 }
     89             }
     90             //for(int j=1;j<=K;j++) printf("(%d,%d) ",tmp[j][0],tmp[j][1]);cout<<endl;
     91             for(int j=1;j<=K;j++) {                //tmp位于前可以取[1..K] 
     92                 if(tmp[j][0]+f[K-j][0]>ans1)
     93                     ans1=tmp[j][0]+f[K-j][0],ans2=0;
     94                 if(tmp[j][0]+f[K-j][0]==ans1)
     95                         ans2+=tmp[j][1]*f[K-j][1];
     96             }
     97             for(int j=1;j<=K;j++) {
     98                 if(tmp[j][0]>f[j][0]) f[j][0]=tmp[j][0],f[j][1]=0;
     99                 if(tmp[j][0]==f[j][0]) f[j][1]+=tmp[j][1];
    100                 tmp[j][1]=tmp[j][0]=0;
    101             }
    102          }
    103      }
    104      //cout<<u<<": "<<ans1<<" "<<ans2<<endl;
    105      for(int j=0;j<=K;j++) f[j][0]=f[j][1]=0;
    106      m=G[u].size();
    107      for(int i=0;i<m;i++) {
    108          int v=G[u][i].v;
    109          if(!vis[v]) {
    110              size=siz[v];
    111              if(siz[v]>siz[u]) siz[v]=S-siz[v];
    112              root=0;
    113              if(size>=K) getroot(v);
    114              solve(root,siz[v]);
    115          }
    116      }
    117 }
    118 void read(int& x) {
    119     char c=getchar(); int f=1; x=0;
    120     while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
    121     while(isdigit(c)) x=x*10+c-'0',c=getchar();
    122     x*=f;
    123 }
    124 int main() {
    125     //freopen("in.in","r",stdin);
    126     //freopen("out.out","w",stdout);
    127     read(n),read(m),read(K); K--;
    128     int u,v,w;
    129     for(int i=0;i<m;i++) {
    130         read(u),read(v),read(w);
    131         g[u].push_back(Edge(v,w));
    132         g[v].push_back(Edge(u,w));
    133     }
    134     for(int i=1;i<=n;i++) 
    135         sort(g[i].begin(),g[i].end());
    136     spfa();
    137     memset(vis,0,sizeof(vis));
    138     dfs(1);
    139     size=n; mx[0]=INF; root=0;
    140     memset(vis,0,sizeof(vis));
    141     getroot(1) , solve(root,size);
    142     printf("%d %d",ans1,ans2);
    143     return 0;
    144 }
  • 相关阅读:
    程序员找工作必备 PHP 基础面试题 (四)
    Laravel 教程:使用Fast Excel解决导出超大 XLSX 文件(千万级)带来的内存问题
    ThinkPHP无限分类的使用
    PHP 的 interface 有什么用处?
    编写可读代码:通过提前返回来减少缩进
    调试事件的派发
    调试对象的构建
    [反汇编分析] 局部变量复用
    [IDA]批量载入结构体
    [反汇编分析]调用函数传入参数不一致时可能寄存器传入参数
  • 原文地址:https://www.cnblogs.com/lidaxin/p/5188253.html
Copyright © 2011-2022 走看看