P2993 [FJOI2014]最短路径树问题 点分治+最短路
这道题还是非常简单的,由于我们要保证最小字典序,因此我们需要把边进行排序,然后从大到小插入,因为链式前向星是倒着存的。我们只需要先跑一个最短路,然后查询边是不是在最短路上,这个可以通过枚举边并用
dist[v]=dist[u]+edge[i]判断即可,如果是的话我们在这个边上打上标记。并进行一次DFS打上标记,保证是一颗树。
然后就简单了,直接点分治查询子树链的长度和节点个树。然后就很更新保存即可,这个查询其实和上个题很相似,用一个mp保存点的个数对应的最长的链,这样就能在枚举子树的链的节点个数的时候,查询是否有其他子树的节点个数,和当前节点的链组成一条合法的链。
。。。为毛找重心在外面开一个maxlink,维护内部最长链会错。。。开数组维护每个节点就不会。。。真奇怪。。。
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> using namespace std; const int INF = 0x3f3f3f3f; const int maxx = 2e5+6; struct edge{ int v,w,next,use; }e[maxx]; int tot,root,n,m,k,l,r,size,ans,ans1; int sz[maxx],vis[maxx],head[maxx],dis[maxx],id[maxx],q[maxx]; int d[maxx],mp[maxx],cnt[maxx],mx[maxx]; struct node{ int u,v,w; }edge[maxx]; struct que{ int len,k; }que[maxx]; void add(int x,int y,int z){ e[++tot].v=y;e[tot].w=z;e[tot].next=head[x];head[x]=tot; e[++tot].v=x;e[tot].w=z;e[tot].next=head[y];head[y]=tot; } void dfs(int x){ vis[x]=1; for (int i=head[x];i;i=e[i].next){ int v=e[i].v; if (e[i].use==1 && !vis[v]){ e[i].use=e[i^1].use=2; dfs(v); } } } void dji(){ priority_queue<pii>q; for (int i=1;i<=n;i++){ dis[i]=INF; } dis[1]=0; q.push(make_pair(0,1)); while(q.size()){ int u=q.top().second; q.pop(); if (vis[u])continue; vis[u]=1; for (int i=head[u];i;i=e[i].next){ int v=e[i].v; if (dis[v]>dis[u]+e[i].w){ dis[v]=dis[u]+e[i].w; q.push(make_pair(-dis[v],v)); } } } for (int i=2;i<=tot;i++){ int u=e[i^1].v; int v=e[i].v; if (dis[v]==dis[u]+e[i].w){ e[i].use=1; } } memset(vis,0,sizeof(vis)); dfs(1); } void getroot(int x,int fa) { sz[x]=1;mx[x]=0; for (int i=head[x];i;i=e[i].next) { if (e[i].use<2||vis[e[i].v]||e[i].v==fa) continue; getroot(e[i].v,x); sz[x]+=sz[e[i].v]; mx[x]=max(mx[x],sz[e[i].v]); } mx[x]=max(mx[x],size-sz[x]); if (!root||mx[x]<mx[root]) root=x; } void getdis(int u,int num,int dist,int fa) { //cout<<u<<"orz"<<num<<endl; if (num<=k)que[++r].k=num,que[r].len=dist; for (int i=head[u];i;i=e[i].next) { int v=e[i].v; // cout<<v<<" "<<e[i].use<<" "<<vis[v]<<" "<<fa<<" "<<num<<endl; if (e[i].use<2 || vis[v] || v==fa)continue; getdis(v,num+1,dist+e[i].w,u); } } void slove(int u){ vis[u]=1; r=0; for (int i=head[u];i;i=e[i].next) { int v=e[i].v; if (e[i].use<2||vis[v])continue; int tmp=r; getdis(v,1,e[i].w,u); for (int j=tmp+1;j<=r;j++) { int dist=que[j].len; int num=que[j].k; if (mp[k-num]>-1) { if (dist+mp[k-num]>ans)ans=dist+mp[k-num],ans1=cnt[k-num]; else if (dist+mp[k-num]==ans)ans1+=cnt[k-num]; } } for (int j=tmp+1;j<=r;j++) { int num=que[j].k,dist=que[j].len; if (dist>mp[num])mp[num]=dist,cnt[num]=1; else if (dist==mp[num])cnt[num]++; } } while(r)mp[que[r].k]=-1,cnt[que[r].k]=0,r--; for (int i=head[u];i;i=e[i].next) { int v=e[i].v; if (e[i].use<2||vis[v])continue; root=0; size=sz[v]; getroot(v,u); slove(root); } } bool cmp(node a,node b){ if (a.u==b.u){ if (a.v==b.v){ return a.w<b.w; } return a.v<b.v; } return a.u<b.u; } int main(){ scanf("%d%d%d",&n,&m,&k); k--; for (int i=1;i<=m;i++){ scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w); } sort(edge+1,edge+1+m,cmp); tot=1; memset(cnt,0,sizeof(cnt)); memset(head,0,sizeof(head)); for (int i=m;i>=1;i--){ add(edge[i].u,edge[i].v,edge[i].w); } dji(); memset(vis,0,sizeof(vis)); root=0; size=n; getroot(1,0); for (int i=1;i<=k;i++)mp[i]=-1; cnt[0]=1; slove(root); printf("%d %d\n",ans,ans1); return 0; }
有不懂欢迎咨询
QQ:1326487164(添加时记得备注)