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;
}

  

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章