• 洛谷P2194 HXY烧情侣


    题目描述

    众所周知,(HXY)已经加入了(FFF)团。现在她要开始喜((sang))((xin))((bing))((kuang))地烧情侣了。这里有(n)座电影院,(n)对情侣分别在每座电影院里,然后电影院里都有汽油,但是要使用它需要一定的费用。(m)条单向通道连接相邻的两对情侣所在电影院。然后(HXY)有个绝技,如果她能从一个点开始烧,最后回到这个点,那么烧这条回路上的情侣的费用只需要该点的汽油费即可。并且每对情侣只需烧一遍,电影院可以重复去。然后她想花尽可能少的费用烧掉所有的情侣。问最少需要多少费用,并且当费用最少时的方案数是多少?由于方案数可能过大,所以请输出方案数对(1e9+7)取模的结果。

    (注:这里(HXY)每次可以从任何一个点开始走回路。就是说一个回路走完了,下一个开始位置可以任选。所以说不存在烧不了所有情侣的情况,即使图不连通,(HXY)自行选择顶点进行烧情侣行动。且走过的道路可以重复走。)

    输入输出格式

    输入格式:

    第一行,一个整数(n)

    第二行,(n)个整数,表示(n)个情侣所在点的汽油费。

    第三行,一个整数(m)

    接下来(m)行,每行两个整数(x_i,y_i),表示从点xi可以走到(y_i)

    输出格式:

    一行,两个整数,第一个数是最少费用,第二个数是最少费用时的方案数对(1e9+7)取模

    输入输出样例

    输入样例#1:

    3
    1 2 3
    3
    1 2
    2 3
    3 2
    

    输出样例#1:

    3 1
    

    输入样例#2:

    3
    10 20 10
    4
    1 2
    1 3
    3 1
    2 1
    

    输出样例#2:

    10 2
    

    说明

    数据范围:

    对于(30\%)的数据,(1<=n,m<=20)

    对于(10\%)的数据,保证不存在回路。

    对于(100\%)的数据,(1<=n<=100000,1<=m<=300000)。所有输入数据保证不超过(10^9)

    思路:考虑tarjan缩点,把一个强连通分量缩成一个点的同时更新这个强连通分量里汽油费的最小值,最后每个强连通分量的最小值的和就是第一个子问题的答案,然后看看每个联通块中有多少个权值是它那个更新出来的最小值,根据乘法原理,把每个强连通分量得到结果乘起来就是第二个子问题的答案。

    代码:

    #include<cstdio>
    #include<algorithm>
    #include<cctype>
    #include<stack>
    #define maxn 100007
    #define ll long long
    using namespace std;
    const ll mod=1e9+7;
    int n,m,head[maxn],w[maxn],dfn[maxn],block[maxn],low[maxn],bel[maxn],cnt,js,num,ans;
    bool vis[maxn];
    ll zrj=1,size[maxn];
    struct node{
      int v,nxt;
    }e[300007];
    inline void ct(int u, int v) {
      e[++num].v=v;
      e[num].nxt=head[u];
      head[u]=num;
    }
    stack<int>q;
    void tarjan(int u) {
      dfn[u]=low[u]=++cnt;
      q.push(u),vis[u]=1;
      for(int i=head[u];i;i=e[i].nxt) {
        int v=e[i].v;
        if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
        else if(vis[v]) low[u]=min(low[u],dfn[v]);
      }
      if(dfn[u]==low[u]) {
        int x=-1;js++;
        block[js]=w[u];
        while(x!=u) {
          x=q.top(),q.pop();
          block[js]=min(block[js],w[x]);
          bel[x]=js,vis[x]=0;
        }
      }
    }
    int main() {
      scanf("%d",&n);
      for(int i=1;i<=n;++i) scanf("%d",&w[i]);
      scanf("%d",&m);
      for(int i=1,u,v;i<=m;++i) {
        scanf("%d%d",&u,&v);
        ct(u,v);
      }
      for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
      for(int i=1;i<=js;++i) ans+=block[i];
      printf("%d ",ans);
      for(int i=1;i<=n;++i) if(w[i]==block[bel[i]]) size[bel[i]]++;
      for(int i=1;i<=js;++i) {
        zrj*=size[i];
        zrj%=mod;
      }
      printf("%d
    ",zrj);
      return 0;
    }
    
  • 相关阅读:
    free和delete把指针怎么啦?
    动态内存会被自动释放吗?
    杜绝“野指针”
    有了malloc/free为什么还要new/delete ?
    Linux Shell Bash 带有特殊含义的退出码
    centos中更换jdk的版本
    shift移动变量
    shell script针对参数已经有配置好变量名称
    输入两个数后输出相乘的结果
    输入变量
  • 原文地址:https://www.cnblogs.com/grcyh/p/10142876.html
Copyright © 2020-2023  润新知