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

这道题的思想很好
建出最短路径树然后进行点分
每次用两个数组来记录

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;


char c;
inline void read(int &a)
{
    a=0;do c=getchar();while(c<'0'||c>'9');
    while(c<='9'&&c>='0')a=(a<<3)+(a<<1)+c-'0',c=getchar();
}

struct No
{
    int u,v;
    int Dis,len;
    inline friend bool operator <(No a,No b)
    {return a.Dis>b.Dis;}
};
priority_queue<No>Q;
int Dis[1000001];


struct Chain
{
    int u,len;
    Chain *next;
    Chain(){next=NULL;}
}*S[1000001],*Head[1000001];
int F[1000001];

struct Side
{
    int u,len;
    inline friend bool operator <(Side a,Side b)
    {return a.u<b.u;}
};
vector<Side>SS[100001];
inline void Add(int a,int b,Chain **Head,int len)
{
     Chain *tp=new Chain;
     tp->next=Head[a];
     Head[a]=tp;
     tp->len=len;
     tp->u=b;
}

inline void Replace(int a,int b,Chain **Head,int len)
{
     Chain *tp=new Chain;
     tp->next=Head[a];
     Head[a]=tp;
     tp->len=len;
     tp->u=b;
}

inline void Dij(Chain **Head,int S)
{
    while(!Q.empty())Q.pop();
    Q.push((No){S,S,0,0});
   No tp;
    while(!Q.empty())
     {
       tp=Q.top();
       Q.pop();
       if(tp.v!=1&&tp.Dis==Dis[tp.v])
         SS[tp.u].push_back((Side){tp.v,tp.len});
       else if(Dis[tp.v])continue;
       else
          {
            Dis[tp.v]=tp.Dis;
            F[tp.v]=tp.u;
            SS[tp.u].push_back((Side){tp.v,tp.len});
            //Replace(tp.u,tp.v,SS,tp.len);
            for(Chain *t=Head[tp.v];t;t=t->next)
                if(t->u!=S&&!Dis[t->u])
                    Q.push((No){tp.v,t->u,t->len+tp.Dis,t->len});
          }
     }
}

bool Chosen[100001];
int Heavy[100001];

int size[100001];
int root,Max;
bool done[100001];
void Build(int u)
{
    size[u]++;
    sort(SS[u].begin(),SS[u].end());
    for(int i=0;i<SS[u].size();i++) 
      if(SS[u][i].u!=u&&!done[SS[u][i].u])
        done[SS[u][i].u]=true,Add(u,SS[u][i].u,Head,SS[u][i].len),Add(SS[u][i].u,u,Head,SS[u][i].len),Build(SS[u][i].u),size[u]+=size[SS[u][i].u];
}

void FindRoot(int u,int f)
{
    size[u]=1;
    Heavy[u]=0;
    for(Chain *tp=Head[u];tp;tp=tp->next)
     if(!Chosen[tp->u]&&tp->u!=f)
       {
            FindRoot(tp->u,u);
            Heavy[u]=max(Heavy[u],size[tp->u]);
            size[u]+=size[tp->u];
       }
    Heavy[u]=max(Heavy[u],Max-size[u]);
    if(Heavy[u]<Heavy[root])
        root=u; 
}

int tot;
int L[100001];
struct R
{
  int key,f;
  inline friend bool operator <(R a,R b)
  {return a.key>b.key;}
  inline friend bool operator ==(R a,R b)
  {return a.f==b.f;}

}LP[1000001];


int Op2[1000001][2];
int  Op[1000001][2];
const
   int INF=1<<29;
int K;
void DFS(int x,int dep,int len,int fa)
{
    if(dep>=K)return ;
    if(Op2[dep][0]<len)
       Op2[dep][0]=len,Op2[dep][1]=1;
    else if(Op2[dep][0]==len) 
       Op2[dep][1]++;
    for(Chain *tp=Head[x];tp;tp=tp->next)
      if(tp->u!=fa&&!Chosen[tp->u])
         DFS(tp->u,dep+1,len+tp->len,x);
}

int longest,Kind;
void Divid(int u)
{
    int i,j;
    Op[0][0]=0;
    Op[0][1]=1;
    for(Chain*tp=Head[u];tp;tp=tp->next)
      if(!Chosen[tp->u])
      {
        DFS(tp->u,1,tp->len,u);
        for(i=1;Op2[i][1];i++)
          if(longest<Op2[i][0]+Op[K-i-1][0])
             longest=Op2[i][0]+Op[K-i-1][0],Kind=Op2[i][1]*Op[K-i-1][1];
             else if(longest==Op2[i][0]+Op[K-i-1][0])Kind+=Op2[i][1]*Op[K-i-1][1];
        for(i=1;Op2[i][1];i++)
          if(Op2[i][0]>Op[i][0])  
             Op[i][0]=Op2[i][0],Op[i][1]=Op2[i][1];
            else if(Op[i][0]==Op2[i][0])Op[i][1]+=Op2[i][1];
        for(i=1;Op2[i][1];i++)
          Op2[i][0]=Op2[i][1]=0; 
      }
      for(i=1;Op[i][1]!=-INF;i++)
        Op[i][0]=Op[i][1]=-INF;
    for(Chain*tp=Head[u];tp;tp=tp->next)
      if(!Chosen[tp->u])
        {
             root=0;
             Max=size[tp->u]>size[u]?size[tp->u]-size[u]:size[tp->u];
             FindRoot(tp->u,tp->u);
             Chosen[root]=true;
             Divid(root);
        } 
}
int n,m;
int main()
{
    int i,j,k;
    Heavy[0]=1<<29;
    read(n),read(m),read(K);
    while(m--)
      read(i),read(j),read(k),Add(i,j,S,k),Add(j,i,S,k);
    Dij(S,1);
    done[1]=true;
    Build(1);
    Max=size[1];
    root=0; 
    for(i=1;i<=K;i++)
      Op[i][0]=Op[i][1]=-INF;
    FindRoot(1,1);Chosen[root]=true;
    Divid(root);
    printf("%d %d\n",longest,Kind);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值