• Codeforces Round #529 (Div. 3)题解


    ABCF看到以后立马就会做了,ABC差一点做到都比dreamoon切得快(总罚时比dreamoon少1分钟),主要是我在写C的时候multiset不知道为什么坏掉了。。。改了好长时间,最后换了堆 存起来再reverse输出的。。。DE自闭了好长时间然后发现是傻逼题

    A.给一个abbcccddddeeeee这样的字符串 输出abcde

      int n;
      cin >> n;
      string a, ans;
      cin >> a;
      int cnt = 0;
      for (int i = 0; i < a.length(); i += ++cnt) ans += a[i];
      cout << ans;
    

    B.给一个数组,问任意去掉一个元素以后的最小极差

    肯定去掉最大或者最小的那个,取个min就行了

      int n;
      in, n;
      if (n <= 2) return puts("0"), 0;
      vector<int>a;
      a.resize(n);
      lop0(i, n) in, a[i];
      sort(all(a));
      out, min(a[n-2] - a[0], a[n-1] - a[1]);
    
    

    C.给出n,m,问n能否分成m个都是2的整数次幂的数相加

    首先两个无解条件 1.popcount(n)>m 无解 2.m>n无解(m个数最小是1)

    先把n按二进制位拆出来,不够m个的话,每次拆掉最大的数x,分成两个x/2,用个堆或者multiset啥的搞就行了(我也不知道为啥比赛的时候multiset不能支持重复元素了,然后用堆搞的)

      int x, n;
      cin >> x >> n;
      if (__builtin_popcount(x) > n) return puts("NO"), 0;
      if (n > x) return puts("NO"), 0;
      puts("YES");
      priority_queue<int>ans;
      for (int i = 0; i < 30; ++i) if ((1 << i) & x) ans.push(1 << i);
      while (ans.size() < n) {
        int x = ans.top(); ans.pop();
        ans.push(x >> 1), ans.push(x >> 1);
      }
      vector<int>Ans;
      while (!ans.empty()) Ans.pb(ans.top()), ans.pop();
      reverse(all(Ans));
      for (auto it = Ans.begin(); it != Ans.end(); ++it) out, *it, ' ';  
    

    D.给出一个n个点的有向环和每个点u的出边指向的点v,v的出边指向的点w,(v,w)不一定按照顺序给出

    输出一个合法的顺序

    考虑有三个点 u,v,w(描述同上)如果v给出的两个点里面有w,那么顺序就是<u,v,w>,否则就是<u,w,v>

    pii a[MAXN << 1];
    bool vis[MAXN << 1];
    int main() {
      int n;
      in, n;
      lop1(i, n) in, a[i].xx, a[i].yy;
      vint ans;
      ans.pb(1);
      vis[1] = 1;
      for (int i = 0; i < n; i += 2) {
        int u = a[ans[i]].xx, v = a[ans[i]].yy;
        if (a[u].xx == v || a[u].yy == v) {
          if (!vis[u]) ans.pb(u);
          if (!vis[v]) ans.pb(v);
        }
        else {
          if (!vis[v]) ans.pb(v);
          if (!vis[u]) ans.pb(u);
        }
        vis[u] = vis[v] = 1;
      }
      for (auto it = ans.begin(); it != ans.end(); ++it) out, *it, ' ';
      return 0;
    }
    

    E.给个括号序列,问有多少位置取反后整个序列合法

    1.线段树

    #define ls (o<<1)
    #define rs (o<<1|1)
    char s[MAXN];
    int lv[MAXN << 1], rv[MAXN << 1], n;
    //rv[i] i节点对应区间除掉匹配的括号以外)的个数  lv[i] i节点对应区间除掉匹配的括号以外(的个数
    
    inline void pushup(int o) {
      int m = min(lv[ls], rv[rs]);
      lv[o] = lv[ls] - m + lv[rs];
      rv[o] = rv[rs] - m + rv[ls];
    }
    
    
    inline void build(int o, int l, int r) {
      if (l == r) return (s[l] == '(' ? lv : rv)[o] = 1, void();
      build(ls, l, mid), build(rs, mid + 1, r);
      pushup(o);
      // cerr << l << ' ' << r << ' ' << lv[o] << ' ' << rv[o] << endl;
    }
    inline void Modify(int o, int l, int r, int x) {
      if (l == r) return void(swap(lv[o], rv[o]));
      x <= mid ? Modify(ls, l, mid, x) : Modify(rs, mid+1, r, x);
      pushup(o);
    }
    
    int main() {
    #ifdef LOCAL_DEBUG
      // freopen("data.in", "r", stdin), freopen("data.out", "w", stdout);
      Dbg = 1;
    #endif
      in, n;
      in, s + 1;
      build(1, 1, n);
      if (lv[1] + rv[1] != 2) return puts("0"), 0;
      int ans = 0;
      lop1(i, n) Modify(1, 1, n, i), ans += !(lv[1] + rv[1]), Modify(1, 1, n, i);
      out, ans;
    
    
    
    #ifdef LOCAL_DEBUG
      fprintf(stderr, "
    time:%.5fms", clock() * 1.0 / CLOCKS_PER_SEC * 1000);
    #endif
      return 0;
    }
    

    2.线性做法,cf上很多人写的我看不懂。。。xzz聚聚几分钟就写出了一个线性代码,我这等彩笔还能看懂,orz xzz orz yyb(事xzz让我干的)

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN = 1e6 + 7;
    char S[1000010];
    int pre[1000010], suf[1000010], n;
    int main() {
      scanf("%d%s", &n, S + 1);
      for (int i = 1; i <= n; ++i)
        if (pre[i - 1] == -1) pre[i] = -1;
        else if (S[i] == '(') pre[i] = pre[i - 1] + 1;
        else if (pre[i - 1]) pre[i] = pre[i - 1] - 1;
        else pre[i] = -1;
      for (int i = n; i; --i)
        if (suf[i + 1] == -1) suf[i] = -1;
        else if (S[i] == ')') suf[i] = suf[i + 1] + 1;
        else if (suf[i + 1]) suf[i] = suf[i + 1] - 1;
        else suf[i] = -1;
      int ans = 0;
      for (int i = 1; i <= n; ++i) ans += ~pre[i - 1] && ~suf[i + 1] && !((S[i] == '(' ? -1 : 1) + pre[i - 1] - suf[i + 1]); //前面和后面合法 改掉以后数量相等
      printf("%d
    ", ans);
      return 0;
    }
    

    F.给出一个序列a和m条特殊边,对于一条边,若在m条边中已存在,权值是给出的权值跟a[u]+a[v]取min,否则权值是a[u]+a[v],求最小生成树
    m条边以外再push进n条边 <最小的a的位置,i,a[i]+最小的a>
    然后kruskal

    目睹了一位神仙edge越界1e5然后稳稳地AC了此题 orz

    struct Edge {
      int u, v;
      ll w;
      inline bool operator < (const Edge & rhs) const {
        return w < rhs.w;
      }
    } E[400005];
    int fa[200005], n, m;
    ll a[200005];
    inline int Find(int x) {
      return x == fa[x] ? x : fa[x] = Find(fa[x]);
    }
    inline int Min(int x, int y) {
      return a[x] < a[y] ? x : y;
    }
    int main() {
      in, n, m;
      if (n == 1) return puts("0"), 0;
      a[0] = 1e18;
      lop(i, 1, n) in, a[i];
      int Min = min_element(a+1, a+1+n) - a;
      lop(i, 1, m) in, E[i].u, E[i].v, E[i].w;
      lop(i, 1, n) fa[i] = i, E[++m] = (Edge) {Min, i, a[i] + a[Min]};//Min == i也没关系 因为i到i的边不会影响答案
      sort(E + 1, E + 1 + m);
      int cnt = 0; ll ans = 0;
      lop(i, 1, m) {
        int u = Find(E[i].u), v = Find(E[i].v);
        if (u == v) continue;
        fa[u] = v, ans += E[i].w;
        if (++cnt == n - 1) break;
      }
      out, ans;
      return 0;
    }
    
  • 相关阅读:
    面试题58 二叉树的下一个结点
    面试题57 删除链表中重复的结点
    面试题56 链表中环的入口结点
    面试题55 字符流中第一个不重复的字符
    面试题54 表示数值的字符串
    面试题50 树中两个结点的最低公共祖先
    面试题53 正则表达式匹配
    面试题52 构建乘积数组
    面试题51 数组中重复的数字
    Qt链接库出错version Qt_5 not defined
  • 原文地址:https://www.cnblogs.com/storz/p/10192675.html
Copyright © 2020-2023  润新知