BZOJ 5056 OI游戏题解

数据这么水,跑SPFA也不会被卡哈哈哈。

首先看题,题目大意是在一个无向图中求构造不同的最小生成树的方法,对此引发思考:怎样才能满足题意(新图与原图的最短路长度一致)。

想了想,觉得暴力能够水过去,然后写了下T了,这不就是个最短路计数+组合数学乘法原理吗?

想法确定了,怎么写呢?

  1. SPFA跑出最短路
  2. 处理时每当遇到满足(dis[x]==dis[y]+z)的点就该点的ans++
  3. 每处理完一个点就令tot*=ans,记得取mod

这样,答案就出来啦

思路简单,码量较少,解法自然,是一道推荐一做的好题。

代码如下:

#include<bits/stdc++.h>
#define ll long long
#define HA 1000000007
#define ERP(i,a) for(int i=head[(a)];i;i=e[i].nxt)
#define REP(i,a,b) for(int i=(a),edd=(b);i<=edd;++i)
#define DRP(i,a,b) for(int i=(a),edd=(b);i>=edd;--i)
using namespace std;
const int _=51;

inline int read(){
    char ch=getchar();
    int r=0,s=1;
    while(ch>57||ch<48)s=ch==45?0:s,ch=getchar();
    while(ch>=48&&ch<=57)r=r*10+ch-48,ch=getchar();
    return s?r:-r;
}

queue< int > q;
int n;
int g[_][_],dis[_],vis[_],ans[_];

void init(){
    memset(dis,0x7f,sizeof(dis));
    n=read();
    REP(i,1,n)REP(j,1,n)scanf("%1d",&g[i][j]);
    ans[1]=1;
    return;
}

void spfa(int s){
    dis[s]=0,vis[s]=1,q.push(s);
    while(q.size()){
        int x=q.front();vis[x]=0,q.pop();
        REP(i,1,n){
            if(!g[x][i])continue;
            if(dis[i]>dis[x]+g[x][i]){
                dis[i]=dis[x]+g[x][i];
                if(!vis[i])q.push(i),vis[i]=1;
            }
        }
    }
    return;
}

void solve(){
    spfa(1);
    ll tot=1;
    REP(i,2,n){
        REP(j,1,n){
            if(!g[i][j])continue;
            if(dis[i]==dis[j]+g[i][j])ans[i]++;
        }
        tot=(tot*ans[i])%HA;
    }
    printf("%lld\n",tot);
    return;
}

int main(){
    init();
    solve();
    return 0;
}
posted @ 2018-10-27 16:19  绥棱泷Narcissus  阅读(163)  评论(8编辑  收藏  举报