2016湖南省第十二届大学生程序设计竞赛(题解)

B.有向无环图(CSU 1804)

Time Limit: 5 Sec Memory Limit: 128 MB
Submit: 291 Solved: 136
[Submit][Status][Web Board]

Description

Bobo 有一个 n 个点,m 条边的有向无环图(即对于任意点 v ,不存在从点 v 开始、点 v 结束的路径)。

为了方便,点用 1,2,,n 编号。 设 count(x,y) 表示点 x 到点 y 不同的路径数量(规定 count(x,x)=0 ), Bobo 想知道

i=1nj=1mcount(i,j)aibj

除以 (109+7) 的余数。

其中, ai,bj 是给定的数列。

Input

输入包含不超过 15 组数据。

每组数据的第一行包含两个整数 n,m(1n,m105) .

接下来 n 行的第 i 行包含两个整数 ai,bi(0ai,bi109) .

最后 m 行的第 i 行包含两个整数 ui,vi ,代表一条从点 ui vi 的边 (1ui,vin)

Output

对于每组数据,输出一个整数表示要求的值。

Sample Input
3 3
1 1
1 1
1 1
1 2
1 3
2 3
2 2
1 0
0 2
1 2
1 2
2 1
500000000 0
0 500000000
1 2

Sample Output
4
4
250000014

题意

中文题,不解释└(^o^)┘!

解题思路

通过对公式 ni=1mj=1count(i,j)aibj 进行化简优化,我们可以通过这个公式将前面一部分 ni=1count(i,j)ai 给求出来,然后一次乘以 bj 就可以得到答案,如何求出前面一部分?

我设置 d[s1] 为当前顶点已经被处理的所有 count(x,s1) 之和, x 为所有可以到到s1的点,下一个为 d[s2] ,如果 s1 可以到达 s2 ,则 d[s2]=d[s2]+d[s1] ,接下来我们只要控制在计算 d[s2] 时,前面所有可以到达 s2 点都需要处理完,这里我们就可以使用拓扑排序了,一步步处理,当处理 s2 时,所有可以到达 s2 的顶点都被处理了。

代码

时间复杂度: O(|E|)

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <vector>
#include <cctype>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iomanip>
#include <typeinfo>
#include <iostream>
#include <algorithm>
#include <functional>

using namespace std;

#define pb push_back
#define mp make_pair
#define mem(a, x) memset(a, x, sizeof(a))
#define copy(a, b) memcpy(a, b, sizeof(a))
#define lson rt << 1, l, mid
#define rson rt << 1|1, mid + 1, r
#define FIN freopen("input.txt", "r", stdin)
#define FOUT freopen("output.txt", "w", stdout)

typedef long long LL;
typedef pair<int, int > PII;
typedef pair<int, string> PIS;
typedef pair<LL, LL>PLL;
typedef unsigned long long uLL;

template<typename T>
void print (T* p, T* q, string Gap = " ", bool flag = false) {
    int d = p < q ? 1 : -1;
    while (p != q) {
        if (flag) cout << Gap[0] << *p << Gap[1];
        else cout << *p;
        p += d;
        if (p != q && !flag) cout << Gap;
    }
    cout << endl;
}

template<typename T>
void print (const T &a, string bes = "") {
    int len = bes.length();
    if (len >= 2) cout << bes[0] << a << bes[1] << endl;
    else cout << a << endl;
}

template<typename T>
void debug (T* p, T* q, string Gap = " ", bool flag = false) {
#ifndef ONLINE_JUDGE
    int d = p < q ? 1 : -1;
    cout << "Debug out : ";
    while (p != q) {
        if (flag) cout << Gap[0] << *p << Gap[1];
        else cout << *p;
        p += d;
        if (p != q && !flag) cout << Gap;
    }
    cout << endl;
#endif
}

template<typename T>
void debug (const T &a, string bes = "") {
#ifndef ONLINE_JUDGE
    int len = bes.length();
    cout << "Debug out : ";
    if (len >= 2) cout << bes[0] << a << bes[1] << endl;
    else cout << a << endl;
#endif
}

void IO_Init() {
    ios::sync_with_stdio (false);
}

LL LLabs (const LL a) {
    return a >= 0 ? a : -a;
}

const double PI = 3.1415926535898;
const double eps = 1e-10;
const int MAXM = 1e5 + 5;
const int MAXN = 1e5 + 5;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;

/*头文件模板*/

struct o {
    int to, cost, nxt;
} E[MAXN << 1];

int Head[MAXN], tot;
int IN[MAXN], A[MAXN], B[MAXN];
LL D[MAXN];

void init_edge() {
    mem(Head, -1);
    tot = 0;
    mem(IN, 0);
    mem(D, 0);
}

void add_edge(int u, int v, int cost) {
    E[tot].to = v;
    E[tot].cost = cost;
    E[tot].nxt = Head[u];
    Head[u] = tot ++;
}

int n, m, res;

void topu() {
    queue<int>que;
    for(int i = 1; i <= n; i ++) {
        if(!IN[i]) {
            que.push(i);
        }
    }
    while(!que.empty()){
        int u = que.front();
        que.pop();
        for(int v = Head[u]; ~v; v = E[v].nxt){
            o &e = E[v];
            IN[e.to] --;
            D[e.to] = (D[e.to] + D[u] + A[u]) % mod;
            if(!IN[e.to]){
                que.push(e.to);
            }
        }
    }
}

int main() {
#ifndef ONLINE_JUDGE
    FIN;
    //FOUT;
#endif
    IO_Init();
    while(~scanf("%d%d", &n, &m)) {
        init_edge();
        for(int i = 1; i <= n; i ++) {
            scanf("%d%d", &A[i], &B[i]);
        }
        int x, y;
        for(int i = 0; i < m; i ++) {
            scanf("%d%d", &x, &y);
            add_edge(x, y, 1);
            IN[y] ++;
        }
        topu();
        LL res = 0;
        for(int i = 1;i <= n;i ++){
            res = (res + D[i] * B[i] % mod) % mod;
        }
        printf("%lld\n", res);
    }

    return 0;
}

G.Parenthesis(CSU 1809)

Time Limit: 5 Sec Memory Limit: 128 MB
Submit: 812 Solved: 213
[Submit][Status][Web Board]

Description

Bobo has a balanced parenthesis sequence P=p1p2pn of length n and q questions.

The i -th question is whether P remains balanced after pai and pbi swapped. Note that questions are individual so that they have no affect on others.

Parenthesis sequence S is balanced if and only if:

  1. S is empty;

    • or there exists balanced parenthesis sequence A,B such that S=AB ;

    • or there exists balanced parenthesis sequence S such that S=(S) .

    • Input

      The input contains at most 30 sets. For each set:

      The first line contains two integers n,q(2n105,1q105) .

      The second line contains n characters p1p2pn.

      The i -th of the last q lines contains 2 integers ai,bi(1ai,bin,aibi).

      Output

      For each question, output “ Yes ” if P remains balanced, or “No” otherwise.

      Sample Input
      4 2
      (())
      1 3
      2 3
      2 1
      ()
      1 2

      Sample Output
      No
      Yes
      No

      题意

      给定一个长度为 n 的由’(’和’ ) ’两种字符构成的序列,初始序列时平衡即,左括号和右括号最开始是匹配,然后进行q次询问,交换 a b两个位置的字符,判断这个序列是否依旧平衡,每一次询问相互独立,互不影响。

      解题思路

      最开始先求解这个字符串的前缀和,’ ( ’代表加1,’ ) ’代表减1,很明显,然后我们用线段树维护这个前缀和中的最小值,很明显,当一个序列中的前缀和的最小值小于 0 的话,它一定无法组成一个平衡的序列,因为’)’括号多了一个,之后不管是加’ ) ’还是’(’,都无法让前面的’ ) ’找到它匹配的结果,让最终的序列平衡。

      然后我们针对给定交换两个的位置的字符进行判断,分为以下四种情况,当然这四种情况中ab

      1. 当前 a=( b=) ,交换这两个位置的字符跟没有交换一样,因为整个字符串没有变化,而最开始字符串是平衡的,所以前缀和中不会出现小于 0 的情况,所以输出Yes.

      2. 当前 a=) b=) ,交换这两个位置的字符跟没有交换一样,因为整个字符串没有变化,而最开始字符串是平衡的,所以前缀和中不会出现小于 0 的情况,所以输出Yes.

      3. 当前 a=) b=( ,交换这两个位置的字符是不会得到一个前缀和小于 0 ,因为’)’本身是造成减’ 1 ’,如果交换了’(’,那么就变成加 1 ,而将’)’换到后方进行减 1 ,很明显这个操作是相互抵消的,同时不会出现小于0,所以输出 Yes .

      4. 当前 a=( b=) ,正如 3 所说,因为’(’本身是造成加’ 1 ’,如果交换了’)’,那么就变成减 1 ,这一步操作就可能造成前缀和小于0,如此我们需要对最小值进行减 2 操作,判断是否会出现小于0。小于 0 输出No,不小于 0 输出Yes.

      代码

      时间复杂度: O(nlog(n)+qlog(q))

      #include <map>
      #include <set>
      #include <cmath>
      #include <ctime>
      #include <queue>
      #include <stack>
      #include <vector>
      #include <cctype>
      #include <cstdio>
      #include <string>
      #include <cstring>
      #include <sstream>
      #include <cstdlib>
      #include <iomanip>
      #include <typeinfo>
      #include <iostream>
      #include <algorithm>
      #include <functional>
      
      using namespace std;
      
      #define pb push_back
      #define mp make_pair
      #define mem(a, x) memset(a, x, sizeof(a))
      #define copy(a, b) memcpy(a, b, sizeof(a))
      #define lson rt << 1, l, mid
      #define rson rt << 1|1, mid + 1, r
      #define FIN freopen("input.txt", "r", stdin)
      #define FOUT freopen("output.txt", "w", stdout)
      
      typedef long long LL;
      typedef pair<int, int > PII;
      typedef pair<int, string> PIS;
      typedef pair<LL, LL>PLL;
      typedef unsigned long long uLL;
      
      template<typename T>
      void print (T* p, T* q, string Gap = " ", bool flag = false) {
          int d = p < q ? 1 : -1;
          while (p != q) {
              if (flag) cout << Gap[0] << *p << Gap[1];
              else cout << *p;
              p += d;
              if (p != q && !flag) cout << Gap;
          }
          cout << endl;
      }
      
      template<typename T>
      void print (const T &a, string bes = "") {
          int len = bes.length();
          if (len >= 2) cout << bes[0] << a << bes[1] << endl;
          else cout << a << endl;
      }
      
      template<typename T>
      void debug (T* p, T* q, string Gap = " ", bool flag = false) {
      #ifndef ONLINE_JUDGE
          int d = p < q ? 1 : -1;
          cout << "Debug out : ";
          while (p != q) {
              if (flag) cout << Gap[0] << *p << Gap[1];
              else cout << *p;
              p += d;
              if (p != q && !flag) cout << Gap;
          }
          cout << endl;
      #endif
      }
      
      template<typename T>
      void debug (const T &a, string bes = "") {
      #ifndef ONLINE_JUDGE
          int len = bes.length();
          cout << "Debug out : ";
          if (len >= 2) cout << bes[0] << a << bes[1] << endl;
          else cout << a << endl;
      #endif
      }
      
      void IO_Init() {
          ios::sync_with_stdio (false);
      }
      
      LL LLabs (const LL a) {
          return a >= 0 ? a : -a;
      }
      
      const double PI = 3.1415926535898;
      const double eps = 1e-10;
      const int MAXM = 1e5 + 5;
      const int MAXN = 1e5 + 5;
      const int INF = 0x3f3f3f3f;
      const int mod = 1e9 + 7;
      
      /*头文件模板*/
      
      int n, q;
      
      int sum[MAXN << 2];
      char S[MAXN];
      int A[MAXN];
      int tot;
      
      void pushup(int rt) {
          sum[rt] = min(sum[rt << 1], sum[rt << 1 | 1]);
      }
      
      void build(int rt, int l, int r) {
          if(l == r) {
              sum[rt] = A[tot];
              tot ++;
              return;
          }
          int mid = (l + r) >> 1;
          build(lson);
          build(rson);
          pushup(rt);
      }
      
      int query(int L, int R, int rt, int l, int r) {
          if(L > R) return 0;
      
          if(L <= l && r <= R) {
              return sum[rt];
          }
          int mid = (l + r) >> 1;
          int res = INF;
          if(L <= mid) {
              res = min(res, query(L, R, lson));
          }
          if(mid < R) {
              res = min(res, query(L, R, rson));
          }
          return res;
      }
      
      
      int main() {
      #ifndef ONLINE_JUDGE
          //FIN;
          //FOUT;
      #endif
          IO_Init();
          while(~scanf("%d%d", &n, &q)) {
      
              tot = 1;
      
              scanf("%s", S);
              A[0] = 0;
              int len = strlen(S);
              for(int i = 0;i < len;i ++){
                  A[i + 1] = A[i] + (S[i] == '(' ? 1 : -1);
              }
      
              build(1, 1, n);
      
              int l, r;
              for(int i = 0; i < q; i ++) {
                  scanf("%d%d", &l, &r);
      
                  if(r < l) swap(l, r);
      
                  int res1 = query(l, r - 1, 1, 1, n);
      
                  l --, r --;
      
                  if(S[l] == '(' && S[r] == ')'){
                      res1 -= 2;
                  }
      
                  puts(res1 >= 0 ? "Yes" : "No");
              }
          }
          return 0;
      }

      F.地铁(CSU 1808)

      Time Limit: 5 Sec Memory Limit: 128 MB
      Submit: 668 Solved: 150
      [Submit][Status][Web Board]

      Description

      Bobo 居住在大城市 ICPCCamp。

      ICPCCamp 有 n 个地铁站,用 1,2,,n 编号。 m 段双向的地铁线路连接 n 个地铁站,其中第 i 段地铁属于 ci 号线,位于站 ai,bi 之间,往返均需要花费 ti 分钟(即从 ai bi 需要 ti 分钟,从 bi ai 也需要 ti 分钟)。

      众所周知,换乘线路很麻烦。如果乘坐第 i 段地铁来到地铁站 s,又乘坐第 j 段地铁离开地铁站 s,那么需要额外花费 |cicj| 分钟。注意,换乘只能在地铁站内进行。

      Bobo 想知道从地铁站 1 到地铁站 n 所需要花费的最小时间。

      Input

      输入包含不超过 20 组数据。

      每组数据的第一行包含两个整数 n,m(2n105,1m105) .

      接下来 m 行的第 i 行包含四个整数 ai,bi,ci,ti(1ai,bi,cin,1ti109) .

      保证存在从地铁站 1 n 的地铁线路(不一定直达)。

      Output

      对于每组数据,输出一个整数表示要求的值。

      Sample Input
      3 3
      1 2 1 1
      2 3 2 1
      1 3 1 1
      3 3
      1 2 1 1
      2 3 2 1
      1 3 1 10
      3 2
      1 2 1 1
      2 3 1 1

      Sample Output
      1
      3
      2

      题意

      中文题,不解释└(^o^)┘!

      解题思路

      这道题目稍微有点坑,如果单纯的使用最短路去求解,每一段地铁针对前段地铁进行判断求解的话,非常不现实 ,总共要求解将近 m2 的结果,针对题目给定的复杂度,傻眼!!!,如此我们需要想新的办法。

      拆点构图,这个思路稍微想过,在网络流做过相应的题目,主要的方法是针对每一个进行相应相应的拆点构造,一般是将一个点拆分两个点,然后对于在图中的每一个点的边连接的情况进行重新处理。 [ 此方法会在基本图论方法总结上进行详细解说]

      思路步骤:

      1. 首先用拆点构图明确如何拆点连边,拆点连边如下图所示:
        这里写图片描述

        我们将中间的点 v 进行拆分为v1 v2 两点,中间的价值为 |c1c2|

      2. 然后我们就会发现,针对每一个点,只要是和它相连接的顶点我们都需要进行拆分,如下图:
        这里写图片描述
        如此就会扩充大量的边,很明显这样是不行的.

      3. 于是我们就需要对一个顶点相连接的边的数据对 c 进行排序,为什么要对c进行排序,如下图:
        这里写图片描述
        我们发现,我们可以用 |VZ|+|ZA1| 来代替 |VA1| ,这样我们就可以知道,我们不需要每一个点之间都需要构造边,我们只要构造相邻 c 的边进行构建就可以了。

      如此我们就解决了拆点构边的问题。

      我们构造点之后,如何进行最短路处理,由于我们进行拆点构边,每一个点都被重构了一次,我们还需要用到多源多汇的原理,从0点开始连接所有与顶点为 1 ,边的权值为0,同时连接构造点后的总点数与 n ,边的权值为0 [ 与每一个顶点连接的边的c的值具有唯一性 ] <script type="math/tex" id="MathJax-Element-5105">]</script>

      如此题目基本解决,有些不明白的地方可以看代码,里面也有相关讲解。

      代码

      时间复杂度:

      /*头文件模板*/
      
      #include <map>
      #include <set>
      #include <cmath>
      #include <ctime>
      #include <queue>
      #include <stack>
      #include <vector>
      #include <cctype>
      #include <cstdio>
      #include <string>
      #include <cstring>
      #include <sstream>
      #include <cstdlib>
      #include <iomanip>
      #include <typeinfo>
      #include <iostream>
      #include <algorithm>
      #include <functional>
      
      using namespace std;
      
      #define pb push_back
      #define mp make_pair
      #define mem(a, x) memset(a, x, sizeof(a))
      #define copy(a, b) memcpy(a, b, sizeof(a))
      #define lson rt << 1, l, mid
      #define rson rt << 1|1, mid + 1, r
      #define FIN freopen("input.txt", "r", stdin)
      #define FOUT freopen("output.txt", "w", stdout)
      
      typedef long long LL;
      typedef pair<int, int > PII;
      typedef pair<int, string> PIS;
      typedef pair<LL, int>PLI;
      typedef pair<LL, LL>PLL;
      typedef unsigned long long uLL;
      
      template<typename T>
      void print (T* p, T* q, string Gap = " ", bool flag = false) {
          int d = p < q ? 1 : -1;
          while (p != q) {
              if (flag) cout << Gap[0] << *p << Gap[1];
              else cout << *p;
              p += d;
              if (p != q && !flag) cout << Gap;
          }
          cout << endl;
      }
      
      template<typename T>
      void print (const T &a, string bes = "") {
          int len = bes.length();
          if (len >= 2) cout << bes[0] << a << bes[1] << endl;
          else cout << a << endl;
      }
      
      template<typename T>
      void debug (T* p, T* q, string Gap = " ", bool flag = false) {
      #ifndef ONLINE_JUDGE
          int d = p < q ? 1 : -1;
          cout << "Debug out : ";
          while (p != q) {
              if (flag) cout << Gap[0] << *p << Gap[1];
              else cout << *p;
              p += d;
              if (p != q && !flag) cout << Gap;
          }
          cout << endl;
      #endif
      }
      
      template<typename T>
      void debug (const T &a, string bes = "") {
      #ifndef ONLINE_JUDGE
          int len = bes.length();
          cout << "Debug out : ";
          if (len >= 2) cout << bes[0] << a << bes[1] << endl;
          else cout << a << endl;
      #endif
      }
      
      void IO_Init() {
          ios::sync_with_stdio (false);
      }
      
      LL LLabs (const LL a) {
          return a >= 0 ? a : -a;
      }
      
      const double PI = 3.1415926535898;
      const double eps = 1e-10;
      const int MAXM = 1e5 + 5;
      const int MAXN = 1e5 + 5;
      const int INF = 0x3f3f3f3f;
      
      /*头文件模板*/
      
      struct Edge{
          int to, cost, nxt;
      }E[MAXN << 3];
      
      struct o{
          int c, t, v;
          o(){}
          o(int v, int c, int t):v(v), c(c), t(t){}
          bool operator < (const o &a) const{
              return c < a.c;
          }
      };
      
      int Head[MAXN << 2], tot;
      LL d[MAXN << 3];
      
      vector<o>IN[MAXN];
      vector<int>C_d[MAXN];
      map<int, int>C_d_m[MAXN];
      
      void init_edge(){
          mem(Head, -1);
          tot = 0;
          for(int i = 0;i < MAXN;i ++){
              IN[i].clear();
              C_d[i].clear();
              C_d_m[i].clear();
          }
      }
      
      void add_edge(int u,int v, int cost){
          E[tot].to = v;
          E[tot].cost = cost;
          E[tot].nxt = Head[u];
          Head[u] = tot ++;
      }
      
      void BFS(int s){//最短路,代码不解释,你懂得!
          priority_queue<PLI, vector<PLI>, greater<PLI> >que;
          que.push(PLI(0, s));
          mem(d, 0x3f);
          d[s] = 0;
          while(!que.empty()){
              PII p = que.top();
              que.pop();
              int u = p.second;
              if(p.first > d[u]) continue;
              for(int v = Head[u]; ~v; v = E[v].nxt){
                  Edge &e = E[v];
                  if(d[e.to] > d[u] + e.cost){
                      d[e.to] = d[u] + e.cost;
                      que.push(PII(d[e.to], e.to));
                  }
              }
          }
      }
      
      
      int n, m;
      
      int main() {
      #ifndef ONLINE_JUDGE
          //FIN;
          //FOUT;
      #endif
          IO_Init();
          int u, v, c, t;
          while(~scanf("%d%d", &n, &m)){
              init_edge();
              for(int i = 0;i < m;i ++){
                  scanf("%d%d%d%d", &u, &v, &c, &t);
                  IN[u].pb(o(v, c, t));
                  IN[v].pb(o(u, c, t));
              }
      
              int zt = 1;
      
              for(int i = 1;i <= n;i ++){
                  sort(IN[i].begin(), IN[i].end());//对每一个顶点连接的边的$c$进行排序。
                  for(int j = 0;j < IN[i].size();j ++){
                      C_d_m[i][IN[i][j].c] = zt;//获取得到每一个c对应的新构造的顶点
                      C_d[i].pb(zt ++);
                  }
              }
      
              for(int i = 0;i < IN[1].size();i ++){
                  add_edge(0, C_d[1][i], 0);//连接顶点为0到与1相连的顶点
              }
      
              for(int i = 0;i < IN[n].size();i ++){
                  add_edge(C_d[n][i], zt, 0);//连接与n相连接的顶点到zt顶点
              }
      
              for(int i = 1;i <= n;i ++){
                  for(int j = 0;j < IN[i].size();j ++){
                      u = i, v = IN[i][j].v, c = IN[i][j].c, t = IN[i][j].t;
      
                      add_edge(C_d_m[v][c], C_d[i][j], t);//连接相邻的两个点
      
                      if(j >= 1){//开始构造满足条件的边
                          add_edge(C_d[i][j - 1], C_d[i][j], IN[i][j].c - IN[i][j - 1].c);
                          add_edge(C_d[i][j], C_d[i][j - 1], IN[i][j].c - IN[i][j - 1].c);
                      }
                  }
              }
      
              BFS(0);
      
              printf("%lld\n", d[zt]);
          }
          return 0;
      }
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值