Description
给一个包含n个点,m条边的无向连通图。从顶点1出发,往其余所有点分别走一次并返回。
往某一个点走时,选择总长度最短的路径走。若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径A为1,32,11,路径B为1,3,2,11,路径B字典序较小。注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小)。到达该点后按原路返回,然后往其他点走,直到所有点都走过。
可以知道,经过的边会构成一棵最短路径树。请问,在这棵最短路径树上,最长的包含K个点的简单路径长度为多长?长度为该最长长度的不同路径有多少条?
这里的简单路径是指:对于一个点最多只经过一次的路径。不同路径是指路径两端端点至少有一个不同,点A到点B的路径和点B到点A视为同一条路径。
题意:如上
解:先按字典序建出最短路树,然后点分治,算出过当前点长为k的简单路径的最大长度和数量。具体通过bfs得出当前点当前子树下包含k个点及以内的最大长度和数量,然后与当前点之前计算完的子树进行计算,得出到当前子树的最大长度和数量。然后递归下去。
还有一个小小的剪枝,是当当前点的size小于k时直接return,因为此时一定无法更新答案,然后我的代码从1084ms降到了648ms。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<queue>
#include<vector>
#define Rep(i,u) for(register int i=Begin[u],v=to[i];i;i=Next[i],v=to[i])
#define For(i,j,k) for(register int i=(j);i<=(int)k;i++)
#define Forr(i,j,k) for(register int i=(j);i>=(int)k;i--)
#define Set(a,b) memset((a),(b),sizeof(a));
#define pb push_back
#define ll unsigned long long
using namespace std;
const int N=30010,M=60010,INF=0x3f3f3f3f;
int Begin[N],to[M],Next[M],w[M],p[N],fa[N],siz[N],n,k,m,e=1,dis[N],dep[N],d[N],tmp[N][2],f[N][2];
struct edge{
int v,w;
edge(int v=0,int w=0):v(v),w(w){}
bool operator <(const edge &r)const{
return v<r.v;
}
};
vector<edge>G[N];
queue<int>q;
inline void add(int x,int y,int z){
to[++e]=y,Next[e]=Begin[x],Begin[x]=e,w[e]=z;
}
void read(int &x){
x=0;char c=getchar();int f=0;
while(c<'0'||c>'9'){c=getchar();if(c=='-')f=1;}
while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();if (f)x=-x;
}
void spfa(int st){
Set(d,INF);
q.push(st),d[st]=0,p[st]=1;
while(!q.empty()){
int u=q.front(),S=G[u].size();q.pop();p[u]=0;
For(i,0,S-1){
int v=G[u][i].v;
if (d[v]>d[u]+G[u][i].w){
d[v]=d[u]+G[u][i].w;
if(!p[v])p[v]=1,q.push(v);
}
}
}
}
void gettree(int u){
int S=G[u].size();
p[u]=1;
For(i,0,S-1){
int v=G[u][i].v;
if (!p[v]&&d[v]==d[u]+G[u][i].w)
add(u,v,G[u][i].w),add(v,u,G[u][i].w),gettree(v);
}
}
void init(){
read(n),read(m),read(k);k--;
For(i,1,m){
int u,v,w;
read(u),read(v),read(w);
G[u].pb(edge(v,w)),G[v].pb(edge(u,w));
}
For(i,1,n)sort(G[i].begin(),G[i].end());
spfa(1);Set(p,0);
gettree(1);Set(p,0);
}
void getcg(int rt,int sz,int &cg){
int flag(1);
siz[rt]=1;
Rep(i,rt)
if (!p[v]&&fa[rt]!=v){
fa[v]=rt;
getcg(v,sz,cg),siz[rt]+=siz[v];
if (siz[v]>sz>>1)flag=0;
}
if(sz-siz[rt]>sz>>1)flag=0;
if(flag)cg=rt;
}
int ans1,ans2;
void solve(int rt,int sz){
int cg;
fa[rt]=0;f[0][1]=1;
if(sz>=k)getcg(rt,sz,cg);else return;
siz[fa[cg]]=sz-siz[cg];fa[cg]=0;
Rep(i,cg)
if(!p[v]){
while(!q.empty())q.pop();
q.push(v),dep[v]=1,dis[v]=w[i],fa[v]=cg;
while(!q.empty()){
int u=q.front();q.pop();
int K=dep[u];
if(K>k)break;
if (dis[u]>tmp[K][0])tmp[K][0]=dis[u],tmp[K][1]=0;
if (dis[u]==tmp[K][0])tmp[K][1]+=1;
Rep(j,u)
if(!p[v]&&fa[u]!=v)
fa[v]=u,q.push(v),
dep[v]=K+1,dis[v]=dis[u]+w[j];
}
For(j,1,k){
if(tmp[j][0]+f[k-j][0]>ans1)ans1=f[k-j][0]+tmp[j][0],ans2=0;
if(tmp[j][0]+f[k-j][0]==ans1)ans2+=tmp[j][1]*f[k-j][1];
}
For(j,1,k){
if(tmp[j][0]>f[j][0])f[j][0]=tmp[j][0],f[j][1]=0;
if(tmp[j][0]==f[j][0])f[j][1]+=tmp[j][1];
tmp[j][0]=tmp[j][1]=0;
}
}
p[cg]=1;
For(i,0,k)f[i][0]=f[i][1]=0;
Rep(i,cg)
if(!p[v])
solve(v,siz[v]);
}
int main(){
init();
solve(1,n);
printf("%d %d\n",ans1,ans2);
return 0;
}