山东济南彤昌机械科技有限公司 山东济南江鹏工贸游有限公司

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 }

 

posted on 2016-02-13 20:29  hahalidaxin  阅读(555)  评论(0编辑  收藏  举报