• Codeforces Round #551 (Div.2) 题解 (翻车记)


    Codeforces Round #551 (Div.2) 题解

    (Out of Competition) 选手翻车记……

    可能这场比赛自己也没怎么认真打,然后……然后就翻车了……

    (C)题之后的题写写题解吧:

    C. Serval and Parenthesis Sequence

    一句话题意:给出字符串包含'(', ')', '?',求出一种将'?'替换成'('或者')'的方案,满足这个字符串所有前缀都不是合法括号序列,整个字符串是合法括号序列。

    比较显然的贪心吧……
    考虑套路方法,把'('设成(1),')'设成(-1),那么做前缀和时,如果某一处的(sum < 0),说明这个字符串无法成为合法括号序列。否则对于每一个'?',如果可以填'(',那么就填左括号,否则填右括号。如果其中某个非结尾的地方(sum == 0),则说明这个前缀形成了合法的括号序,判断无解即可……

    Code:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 3e5 + 50;
    
    int n, c1, c2;
    char s[N];
    int sum = 0;
    
    int main() {
      scanf("%d", &n);
      if(n & 1) return puts(":("), 0;
      scanf("%s", s + 1);
      for(int i = 1; i <= n; i++) {
        if(s[i] == '(') c1++;
        else if(s[i] == ')' ) c2++;
      }
      for(int i = 1; i <= n; i++) {
        if(s[i] == '(') {
          sum ++;
        }
        else if(s[i] == ')') {
          sum --;
        }
        else {
          if(c1 == n / 2) sum--, c2++, s[i] = ')';
          else sum++, c1++, s[i] = '(';
        }
        if(i != n && sum <= 0) return puts(":("), 0;
        if(i == n && sum) return puts(":("), 0;
      }
      for(int i = 1; i <= n; i++) printf("%c", s[i]);
      puts("");
      return 0;
    }
    

    D. Serval and Rooted Tree

    一句话题意:一个(n)个节点的树,每个节点的值是其所有孩子节点的权值的(min)或者(max)。假设有(k)个叶子节点,要求给每个叶子节点权值赋成(1)(k)中的某一个(叶子节点权值不能相同),最大化根节点权值。

    又是一道水题……然而刚开始居然没有想法,看来自己真的智商下降了……写(E)的时候想出来了,但是(E)已经写到一半了,想着(E)分高,就打算先把(E)写出来,再写(D)。然后……就翻车了……

    考虑(dp[x])记录(x)节点能够取到的权值的排名,(v)(x)的子节点,如果(f[x] = 1),那么(dp[x] = min_v dp[v]),否则(dp[x] = sum_v dp[v])。这个还是比较显然的,那么答案就是(n - dp[1] + 1)

    啊啊啊自己真是菜爆了……

    Code:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 3e5 + 50;
    const int INF = 1e7;
    typedef vector<int> Vec;
    
    int n, c;
    int f[N], dp[N];
    Vec G[N];
    
    void Dfs(int o) {
      if(!G[o].size()) return (void) ( dp[o] = 1, c++);
      for(int i = 0; i < (int) G[o].size(); i++) {
        int to = G[o][i];
        Dfs(to);
      }
      if(f[o] == 1) {
        dp[o] = INF;
        for(int i = 0; i < (int) G[o].size(); i++) {
          int to = G[o][i];
          dp[o] = min(dp[o], dp[to]);
        }
      }
      else {
        dp[o] = 0;
        for(int i = 0; i < (int) G[o].size(); i++) {
          int to = G[o][i];
          dp[o] += dp[to];
        }
      }
      return ;
    }
    
    int main() {
      scanf("%d", &n);
      for(int i = 1; i <= n; i++) scanf("%d", &f[i]);
      for(int i = 2, x; i <= n; i++) scanf("%d", &x), G[x].push_back(i);
      Dfs(1);
      int ans = c - dp[1] + 1; 
      printf("%d
    ", ans);
      return 0;
    }
    

    E. Serval and Snake

    一句话题意:emm……似乎有点难说明啊……将就着看一下题吧……

    题解:比较好想的一个二分 + 枚举吧……首先我们从左向右枚举答案端点的列(i),查询((1, 1))((n, i))的答案,如果某次询问的答案为奇数,说明一个端点的纵坐标为(i),另一个端点也类似。而如果所有的询问都是偶数,那么说明两个端点的纵坐标相同。

    先考虑第一种情况,现在我们知道了两个端点的纵坐标(y),我们就可以在横坐标上二分,如果查询((1, y))((mid, y))的答案为奇数,说明端点在([l, mid])里,否则在([mid + 1, r])里。这样加上第一次的枚举,总的次数为(n + log_2 n = 1010)

    然后是第二种情况,我们继续枚举行,查询((1, 1))((i, n))的答案,如果某次的答案为奇数,说明某个端点的横坐标为(i),另一个端点也类似。然后列仍然用二分即可。粗略的考虑一下这样的查询的个数最多似乎是(2 * n + 2 * log_n = 2020)次,超出了限制……但是实际上第一次枚举最多只会有(n - 1),第二次枚举最多只会有(n - 2),而两次二分实际上可以只需要一次,所以询问次数不会超过(2007)次。而且这个很难卡的……

    Code:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1005;
    
    struct Point {
      int x, y;
      void read() {
        scanf("%d%d", &x, &y);
      }
    };
    
    typedef pair<Point, Point> P;
    #define fi first
    #define se second
    #define mk make_pair
    
    int n;
    
    int Ask(Point a, Point b) {
      printf("? %d %d %d %d
    ", a.x, a.y, b.x, b.y);
      fflush(stdout);
      int x;
      scanf("%d", &x);
      if(x == -1) assert(0);
      return x;
    }
    
    void Answer(Point a, Point b) {
      printf("! %d %d %d %d
    ", a.x, a.y, b.x, b.y);
      exit(0);
    }
    
    int main() {
      scanf("%d", &n);
      Point Ansx, Ansy;
      int l = 1, r = n;
      int f = 0;
      for(int i = 1; i <= n; i++) {
        Point A, B;
        A.x = A.y = 1; B.x = n, B.y = i;
        int c = Ask(A, B);
        if(c & 1) {
          f = 1;
          Ansx.y = i;
          break;
        }
      }
      if(!f) {
        for(int i = 1; i <= n; i++) {
          Point A, B;
          A.x = A.y = 1; B.x = i, B.y = n;
          int c = Ask(A, B);
          if(c & 1) {
    	    Ansx.x = i;
    	    break;
          }
        }
        int l = 1, r = n;
        while(l < r) {
          if(l + 1 == r) {
    	    Point A, B;
    	    A.x = A.y = 1; B.x = Ansx.x, B.y = l;
    	    int c = Ask(A, B);
    	    if(c & 1) r--;
    	    else l++;
    	    break;
          }
          int mid = (l + r) >> 1;
          Point A, B;
          A.x = A.y = 1; B.x = Ansx.x, B.y = mid;
          int c = Ask(A, B);
          if(!(c & 1)) l = mid + 1;
          else r = mid;
        }
        Ansx.y = Ansy.y = l;
        l = Ansx.x + 1, r = n;
        while(l < r) {
          if(l + 1 == r) {
    	    Point A, B;
    	    A.x = r, A.y = 1; B.x = B.y = n;
    	    int c = Ask(A, B);
    	    if(c & 1) l++;
    	    else r--;
    	    break;
          }
          int mid = (l + r) >> 1;
          Point A, B;
          A.x = mid, A.y = 1; B.x = B.y = n;
          int c = Ask(A, B);
          if(!(c & 1)) r = mid - 1;
          else l = mid;
        }
        Ansy.x = l;
        Answer(Ansx, Ansy);
      }
      else {
        for(int i = n; i >= 1; i--) {
          Point A, B;
          A.x = 1, A.y = i; B.x = B.y = n;
          int c = Ask(A, B);
          if(c & 1) {
    	    Ansy.y = i;
    	    break;
          }
        }
        int l = 1, r = n;
        while(l < r) {
          if(l + 1 == r) {
    	    Point A, B;
    	    A.x = 1; A.y = Ansx.y; B.x = l, B.y = Ansx.y;
    	    int c = Ask(A, B);
    	    if(c & 1) r--;
    	    else l++;
    	    break;
          }
          int mid = (l + r) >> 1;
          Point A, B;
          A.x = 1, A.y = Ansx.y; B.x = mid, B.y = Ansx.y;
          int c = Ask(A, B);
          if(c & 1) r = mid;
          else l = mid + 1;
        }
        Ansx.x = l;
        l = 1, r = n;
        while(l < r) {
          if(l == r) {
    	    Point A, B;
    	    A.x = 1, A.y = Ansy.y; B.x = l; B.y = Ansy.y;
    	    int c = Ask(A, B);
    	    if(c & 1) r--;
    	    else l++;
    	    break;
          }
          int mid = (l + r) >> 1;
          Point A, B;
          A.x = 1, A.y = Ansy.y; B.x = mid, B.y = Ansy.y;
          int c = Ask(A, B);
          if(c & 1) r = mid;
          else l = mid + 1;
        }
        Ansy.x = l;
        Answer(Ansx, Ansy);
      }
      return 0;
    }
    

    F. Serval and Bonus Problem

    一句话题意:在一个长度为(l)的线段上随机(n)条线段,求长度为(l)的线段中至少被(k)个区间覆盖的线段长度之和……

    考虑随机(n)条线段,就相当于随机(2n)个点,而我们就可以把(l)均等分(2n + 1)段,然后把随机点看成随机这些均等分的点,这样对于题目的答案是不变的。那么我们考虑用(dp),计算出有多少条线段被(k)个区间覆盖,那么最后单独计算一下贡献即可。

    我们记(dp[i][j])表示考虑了前(i)个端点,当前没有匹配上的左端点有(j)个,那么转移就很显然了,下一个要么是左端点,要么是右端点,注意边界条件判一下……

    然后我们开始计算贡献,我们枚举每一个长度为(frac{l}{2n + 1})区间,那么这个区间被(k)个区间覆盖的方案数就是(f[i][j] * f[n * 2 - i][j] * j!)。最后由于求的是期望,再除以(f[n * 2][0])即可。

    Code:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 5005;
    const int Md = 998244353;
    
    inline int Add(const int &x, const int &y) { return (x + y >= Md) ? (x + y - Md) : (x + y); }
    inline int Sub(const int &x, const int &y) { return (x - y < 0) ? (x - y + Md) : (x - y); }
    inline int Mul(const int &x, const int &y) { return (ll)x * y % Md; }
    int Powe(int x, int y = Md - 2) {
      int ans = 1;
      for(; y; y >>= 1, x = (ll)x * x % Md) if(y & 1) ans = 1ll * ans * x % Md;
      return ans;
    }
    
    int n, k, l;
    int fac[N], Inv[N], f[N][N];
    
    void Init() {
      fac[0] = Inv[0] = 1;
      for(int i = 1; i < N; i++) fac[i] = Mul(fac[i - 1], i);
      Inv[N - 1] = Powe(fac[N - 1]);
      for(int i = N - 2; i; i--) Inv[i] = Mul(Inv[i + 1], i + 1);
    }
    
    int main() {
      scanf("%d%d%d", &n, &k, &l);
      Init();
      f[0][0] = 1;
      for(int i = 0; i <= (n << 1); i++) {
        for(int j = 0; j <= min(n, i); j++) {
          f[i + 1][j + 1] = Add(f[i + 1][j + 1], f[i][j]);
          if(j) f[i + 1][j - 1] = Add(f[i + 1][j - 1], Mul(f[i][j], j));
        }
      }
      int ans = 0;
      int len = Mul(l, Powe((n << 1) + 1));
      for(int i = 1; i <= (n << 1) - 1; i++) {
        for(int j = k; j <= min(n, i); j++) ans = Add(ans, Mul(f[i][j], Mul(f[(n << 1) - i][j], fac[j])));
      }
      ans = Mul(ans, Powe(f[n << 1][0]));
      ans = Mul(ans, len);
      printf("%d
    ", ans);
      return 0;
    }
    
  • 相关阅读:
    php 判断访问是否是手机或者pc
    SQLSTATE[HY000] [2002] No such file or directory
    No input file specified.
    Call to undefined function openssl_decrypt()
    Parse error: syntax error, unexpected 'class' (T_CLASS)
    tp5关联模型进行条件查询
    windows下php7.1安装redis扩展以及redis测试使用全过程
    SourceTree跳过初始设置
    对象数组(JSON) 根据某个共同字段 分组
    SDUT 3377 数据结构实验之查找五:平方之哈希表
  • 原文地址:https://www.cnblogs.com/Apocrypha/p/10707367.html
Copyright © 2020-2023  润新知