zoukankan      html  css  js  c++  java
  • 洛谷P1261

    Description

    给定 (n) 个点,(m) 条边。每个点 (i) 有权值 (r(i))

    定义点 (v) 对点 (w) 感兴趣,当且仅当不存在任何一个点 (u) 满足 (r(u)>r(w))(delta(v,u)le delta(v,w))

    其中 (delta(i,j)) 表示点 (i) 到点 (j) 的最短路。

    要求求出所有点感兴趣的点的数量之和。

    Solution

    朴素的想法是暴力跑最短路统计答案,在 (nle 30000) 的范围下效率不言而喻。

    通过简单分析可以发现,权值的大小对于是否感兴趣起到了很大的作用。

    具体来说,当一个点权值更大,距离不会更远时,才会对答案有贡献。

    或者说,若点 (a) 对点 (b) 感兴趣,那么点 (a) 要对权值小于 (b) 的点 (c) 感兴趣的条件是 (delta(a,c)<delta(a,b))

    然后,因为跑 (n) 遍最短路效率低下的根本原因是更新次数太多,所以我们可以利用上面的几个条件来减少更新次数,具体的,就是通过怎么增加限制条件减少一个更新过的点出队后再入队的次数。

    发现数据范围 (1le r(i)le 10) 非常小,所以我们可以从权值入手。

    我们设 (d_{v,k}) 表示离点 (k) 最近的权值大于等于 (v) 的点到 (k) 的距离。

    那么点 (u) 对点 (v) 感兴趣的条件是 (d_{r(v)+1,u}>delta(u,v))

    根据 SPFA 的松弛操作,假设我们从点 (a) 扩展到点 (b)(b) 不对 (a) 感兴趣时,从点 (b) 又扩展到了点 (c),我们会有如下条件:

    [largeegin{cases}d_{r(a)+1,b}ledelta(a,b)\delta(a,c)=delta(a,b)+delta(b,c)end{cases} ]

    根据这个条件可得:

    [largedelta(a,c)ge d_{r(a)+1,b}+delta(b,c) ]

    [Rightarrow largedelta(a,c)ge d_{r(a)+1,c} ]

    所以由上面的分析可以推断,当点 (a) 扩展出的点 (b)(a) 不感兴趣时,点 (b) 扩展出的点也都对 (a) 不敢兴趣,即对答案无贡献。

    根据这个条件,我们在遇到不感兴趣的点后停止扩展,可以极大的提高效率。

    至于 (d) 的求法,先跑最短路处理出每种权值下的部分点,对于无法处理的点,继承上一个点的即可。

    Code

    void spfa1(int x){
      memset(d[x],INF,sizeof d[x]);
      memset(vis,false,sizeof vis);
      queue<int> q;
      for(int i=1;i<=n;i++){
        if(val[i]^x) continue;
        d[x][i]=0;q.push(i);
      }
      while(!q.empty()){
        int u=q.front();
        q.pop();vis[u]=0;
        for(int i=head[u];i;i=e[i].nxt){
          int to=e[i].to;
          if(d[x][u]+e[i].dis<d[x][to]){
            d[x][to]=d[x][u]+e[i].dis;
            if(!vis[to])vis[to]=1,q.push(to);
          }
        }
      }
    }
    
    void spfa2(int x){
      memset(Dis,INF,sizeof Dis);
      memset(vis,false,sizeof vis);
      memset(flag,false,sizeof flag);
      queue<int> q;Dis[x]=0;q.push(x);
      while(!q.empty()){
        int u=q.front();
        q.pop();vis[u]=0;
        if(!flag[u]) flag[u]=1,Ans++;
        for(int i=head[u];i;i=e[i].nxt){
          int to=e[i].to;
          if(Dis[to]>Dis[u]+e[i].dis){
            Dis[to]=Dis[u]+e[i].dis;
            if(!vis[to]&&Dis[to]<d[val[x]+1][to])
              vis[to]=1,q.push(to);
          }
        }
      }
    }
    
    int main(){
      n=read();m=read();
      for(int i=1;i<=n;i++)val[i]=read();
      for(int i=1,fr,to,dis;i<=m;i++){
        fr=read();to=read();dis=read();
        add(fr,to,dis);add(to,fr,dis);
      }
      for(int i=1;i<=10;i++) spfa1(i);
      for(int i=9;i>=1;i--)
        for(int j=1;j<=n;j++)
          d[i][j]=min(d[i][j],d[i+1][j]);
      for(int i=1;i<=n;i++) spfa2(i);
      printf("%d
    ",Ans);
      return 0;
    }
    
  • 相关阅读:
    Google的Java常用类库 Guava资料
    Java 理论与实践: 哈希
    7 款开源 Java 反编译工具
    Eclipse传递main函数参数
    Java程序员常用工具类库
    Eclipse 安装插件
    学习Javascript的8张思维导图
    java开发者最常去的20个英文网站
    关于工作效率的心得分享
    ProtoBuf开发者指南
  • 原文地址:https://www.cnblogs.com/KnightL/p/14983348.html
Copyright © 2011-2022 走看看