zoukankan      html  css  js  c++  java
  • 「SOL」NOI2017Day2 T1T2

    就当我没做过这套题

    而且 T3 也不会


    Day2 A. 游戏

    > Link 游戏 - LOJ

    做过 2-sat 的读者应该能够一眼秒出这道题的正解 —— (mathcal O(2^d)) 枚举 x 的取值,其余还没有唯一确定方案的点是一个 2-sat 问题,总复杂度 (mathcal O(2^dm)),边搜索边剪枝,常数很小。

    但是为什么这道题可以出在 NOI 里呢?因为它的细节简直恶心。

    大概说一下我对几个细节的处理方法:

    • ((i,h_i,j,h_j))(S_i=h_i),则该条件无效,在之后的搜索中保证不会访问 (i=S_i) 这种不合法的点。

    • 若条件 ((i,h_i,j,h_j))(h_j=S_j),则说明 (i) 不能取 (h_i);处理方法是建出限制关系的反图,从每个 (ans_i=S_i) 的状态往回找前驱状态,把这些状态都 ban 掉。

    • x 的点本身只有两种取值,因此对于条件 ((i,h_i,j,h_j)),若 (S_i eq ext x),则可以判断「当 (j) 不取 (h_j) 时,(i) 取除了 (h_i,S_i) 之外的值」。

    • 在 tarjan 缩点的过程中在搜到一个已确定方案的点 (v) 时,经过上述处理,此时一定合法,可以跳过对 (v) 的搜索;因为当前状态一定是一个只有两种取值(非真即假)的状态,而在上述第三条中,若 (v) 的取值限制了当前点的值,则当前点必然已经固定取值。


    Day2 B. 蔬菜

    > Link 蔬菜 - LOJ

    第一步非常关键的转化(也是我没想出正解的原因):把一种蔬菜再拆成两种 —— 最后一个和其他。为什么是最后一个而不是第一个?因为最后一个的保质期最长,最容易放下来。

    为了方便理解可以把「其他」再按保质期不同拆成若干种,但是实现代码时显然不会这么实现,存不下。

    于是现在的问题就是「有若干类物品,物品 (i)(C_i) 个,单位价值为 (V_i),只能放在前 (T_i) 个篮子里;每个篮子只能装 (m) 个物品,最大化放入篮子的物品总价值」。这个贪心比较简单,先放单位价值大的物品,能放则放,不能放则跳过。反证即可说明。

    判断能不能放,可以用并查集维护在某个篮子之前最靠后的未满篮子。

    每次做一遍贪心复杂度 (mathcal O(kpm)) 或者 (mathcal O(kpmlog p))

    考虑增加一个篮子,放入的物品会如何变化。结论是在原来已放入物品的基础上再添加。本来可以直接拿拟阵证明,但是我不会,还是用常规的反证方法:假设原本最优解放入了物品 (x),但在新最优解中不可能有物品 (x),说明篮子 (1sim T_x) 中 的所有物品的单位价值都大于 (x),与 (x) 在原本最优解中矛盾。

    所以说,每次询问 (p_i) 其实只是限制了最优解的物品总数不超过 (mp_i)。可以贪心处理出最优解中有 (i) 个((1le ile mcdotmax{p}))物品的最优解,直接输出即可。


    源代码

    点击展开/折叠 game.cpp
    /*Lucky_Glass*/
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    #define con(typ) const typ &
    const int N = 5e4 + 10, M = 1e5 + 10;
    
    inline int rin(int &r) {
      int b = 1, c = getchar(); r = 0;
      while ( c < '0' || '9' < c ) b = c == '-' ? -1 : b, c = getchar();
      while ( '0' <= c && c <= '9' ) r = r * 10 + (c ^ '0'), c = getchar();
      return r *= b;
    }
    inline char rch(char &r) {
      char str[20] = {};
      scanf("%s", str);
      return r = str[0];
    }
    
    int n, m, nd, nrol;
    char str[N];
    int xpos[10], typ[N], mex[5][5], col[N], rol[N];
    
    template<const int P, const int E> struct Graph {
      int head[P], to[E], nxt[E], ncnt;
      void addEdge(con(int) u, con(int) v) {
        int p = ++ncnt;
        to[p] = v, nxt[p] = head[u];
        head[u] = p;
      }
      inline int operator [] (con(int) u) {return head[u];}
    };
    
    Graph<N * 3, M * 3> gr, rg;
    Graph<N * 3, N * 3> bel;
    
    int nscc, ndfn, nstk;
    int dfn[N * 3], low[N * 3], scc[N * 3], stk[N * 3];
    bool bok[N * 3], ban[N * 3];
    
    void init() {
      mex[0][0] = 1, mex[0][1] = 2, mex[0][2] = 1;
      mex[1][0] = 2, mex[1][1] = 0, mex[1][2] = 0;
      mex[2][0] = 1, mex[2][1] = 0, mex[2][2] = 0;
    }
    bool dfs(con(int) u) {
      int iu = u / 3, cu = u % 3;
      if ( cu == typ[iu] ) return false;
      if ( ~col[iu] ) return cu == col[iu];
      col[iu] = cu, rol[++nrol] = iu;
      for (int it = gr[u]; it; it = gr.nxt[it]) {
        int v = gr.to[it];
        if ( !dfs(v) )
          return false;
      }
      return true;
    }
    void tarjan(con(int) u) {
      if ( ~col[u / 3] ) return;
      dfn[u] = low[u] = ++ndfn;
      stk[++nstk] = u, bok[u] = true;
      for (int it = gr[u]; it; it = gr.nxt[it]) {
        int v = gr.to[it];
        if ( ~col[v / 3] ) continue;
        if ( !dfn[v] ) tarjan(v), low[u] = std::min(low[u], low[v]);
        else if ( bok[v] ) low[u] = std::min(low[u], dfn[v]);
      }
      if ( low[u] == dfn[u] ) {
        int tmp = -1; bel.head[++nscc] = 0;
        do {
          tmp = stk[nstk--];
          bok[tmp] = false;
          scc[tmp] = nscc;
          bel.addEdge(nscc, tmp);
        } while ( tmp != u );
      }
    }
    void fixXPos(con(int) d) {
      if ( d > nd ) {
        for (int i = 1; i <= n; ++i) if ( col[i] == -1 )
          for (int c = 0; c < 3; ++c)
            dfn[i * 3 + c] = 0;
        ndfn = nscc = bel.ncnt = 0;
        for (int i = 1; i <= n; ++i) if ( col[i] == -1 ) {
          int p = -1, q = -1;
          for (int c = 0; c < 3; ++c) if ( !ban[i * 3 + c] ) {
            if ( !dfn[i * 3 + c] ) tarjan(i * 3 + c);
            if ( ~p ) q = c;
            else p = c;
          }
          if ( scc[i * 3 + p] == scc[i * 3 + q] )
            return;
        }
        for (int i = 1; i <= nscc; ++i) {
          bool tag = true;
          for (int it = bel[i]; it; it = bel.nxt[it])
            if ( ~col[bel.to[it] / 3] ) {
              tag = false;
              break;
            }
          if ( !tag ) continue;
          for (int it = bel[i]; it; it = bel.nxt[it])
            col[bel.to[it] / 3] = bel.to[it] % 3;
        }
        for (int i = 1; i <= n; ++i)
          putchar('A' + col[i]);
        putchar('
    ');
        exit(0);
      }
      int u = xpos[d];
      if ( ~col[u] ) {
        fixXPos(d + 1);
        return;
      }
      for (int c = 0; c < 3; ++c) {
        int mrol = nrol;
        if ( dfs(u * 3 + c) ) fixXPos(d + 1);
        while ( nrol > mrol ) col[rol[nrol--]] = -1;
      }
    }
    void doBan(con(int) u) {
      ban[u] = true;
      for (int it = rg[u]; it; it = rg.nxt[it] )
        if ( !ban[rg.to[it]] )
          doBan(rg.to[it]);
    }
    int main() {
      // freopen("input.in", "r", stdin);
      init();
      rin(n), rin(nd);
      scanf("%s", str + 1);
      for (int i = 1, tmp = 0; i <= n; ++i)
        if ( str[i] == 'x' )
          xpos[++tmp] = i, typ[i] = -1;
        else
          typ[i] = str[i] - 'a';
      rin(m);
      for (int i = 1; i <= m; ++i) {
        char tmp;
        int u = rin(u), ut = rch(tmp) - 'A', v = rin(v), vt = rch(tmp) - 'A';
        if ( ut == typ[u] ) continue;
        gr.addEdge(u * 3 + ut, v * 3 + vt);
        rg.addEdge(v * 3 + vt, u * 3 + ut);
        if ( ~typ[u] )
          for (int j = 0; j < 3; ++j)
            if ( j != vt ) {
              gr.addEdge(v * 3 + j, u * 3 + mex[typ[u]][ut]);
              rg.addEdge(u * 3 + mex[typ[u]][ut], v * 3 + j);
            }
      }
      for (int i = 1; i <= n; ++i)
        if ( ~typ[i] )
          doBan(i * 3 + typ[i]);
      for (int i = 1; i <= n; ++i)
        if ( ban[i * 3] && ban[i * 3 + 1] && ban[i * 3 + 2] ) {
          printf("-1
    ");
          return 0;
        }
      memset(col, -1, sizeof col);
      fixXPos(1);
      printf("-1
    ");
      return 0;
    }
    
    点击展开/折叠 vegetables.cpp
    /*Lucky_Glass*/
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    #define con(typ) const typ &
    typedef long long llong;
    const int N = 1e5 + 10;
    
    inline int rin(int &r) {
      int b = 1, c = getchar(); r = 0;
      while ( c < '0' || '9' < c ) b = c == '-' ? -1 : b, c = getchar();
      while ( '0' <= c && c <= '9' ) r = r * 10 + (c ^ '0'), c = getchar();
      return r *= b;
    }
    
    struct Item {
      int val, cnt, bad;
      Item() {}
      Item(con(int) _val, con(int) _cnt, con(int) _bad)
      : val(_val), cnt(_cnt), bad(_bad) {}
      static bool cmpToVal(con(Item) p, con(Item) q) {
        return p.val > q.val;
      }
    } itm[N << 1];
    
    struct Dsu {
      int fa[N];
      int fFind(con(int) u) {return fa[u] == u ? u : fa[u] = fFind(fa[u]);}
      bool mMerge(int u, int v) {
        u = fFind(u), v = fFind(v);
        if ( u == v ) return false;
        fa[u] = v;
        return true;
      }
      void init(con(int) siz) {
        for (int i = 1; i <= siz; ++i)
          fa[i] = i;
      }
    } dsu;
    
    int n, m, nq, nitm;
    int ocnt[N];
    llong ans[N * 10];
    
    int main() {
      rin(n), rin(m), rin(nq);
      const int ED = N - 3;
      for (int i = 1; i <= n; ++i) {
        int per, ext, cnt, bad;
        rin(per), rin(ext), rin(cnt), rin(bad);
        int due = bad ? (cnt + bad - 1) / bad : ED;
        itm[++nitm] = Item(per + ext, 1, -due);
        if ( cnt > 1 ) itm[++nitm] = Item(per, cnt - 1, bad);
      }
      std::sort(itm + 1, itm + 1 + nitm, Item::cmpToVal);
      dsu.init(ED);
      int ncnt = 0;
      for (int i = 1; i <= nitm; ++i)
        if ( itm[i].bad >= 0 ) {
          while ( itm[i].cnt ) {
            int due = itm[i].bad ? (itm[i].cnt + itm[i].bad - 1) / itm[i].bad : ED;
            int k = dsu.fFind(due);
            if ( !k ) break;
            ++ocnt[k];
            if ( ocnt[k] == m ) dsu.mMerge(k, k - 1);
            ans[ncnt + 1] = ans[ncnt] + itm[i].val;
            ++ncnt;
            --itm[i].cnt;
          }
        } else {
          int k = dsu.fFind(-itm[i].bad);
          if ( k ) {
            ++ocnt[k];
            if ( ocnt[k] == m ) dsu.mMerge(k, k - 1);
            ans[ncnt + 1] = ans[ncnt] + itm[i].val;
            ++ncnt;
          }
        }
      while ( nq-- ) {
        int day; rin(day);
        printf("%lld
    ", ans[std::min(day * m, ncnt)]);
      }
      return 0;
    }
    

    THE END

    Thanks for reading!

    我也曾 隐约想过 从这世界逃离
    因为人总被缚于不明所以的意义
    生日飘落杏花雨 虫鸣淹没住叹息
    尽数凝固成往昔连存在都无从证明

    ——《我也曾想过一了百了(中文填词)》 By 洛天依

    > Link 我也曾想过一了百了 - Bilibili

    欢迎转载٩(๑❛ᴗ❛๑)۶,请在转载文章末尾附上原博文网址~
  • 相关阅读:
    Maven相关
    System.Math 方法
    css简单实现五角星评分、点赞收藏、展示评分(半颗星、1/3颗星)
    微信小程序动态评分展示/五角星展示/半颗星展示/自定义长度展示
    如何基于layui的laytpl实现数据绑定
    layui表格和弹出框的简单示例
    css超简单实现div页面居中【适合做弹出框】
    如何简单方便的用字体代替页面中常见常用的小图标小按钮(上)
    用css3的cursor:zoom-in/zoom-out实现微博看图片放大镜效果
    完整版ajax+百度echarts实现统计图表demo并随着窗口大小改变而自适应
  • 原文地址:https://www.cnblogs.com/LuckyGlass-blog/p/14801789.html
Copyright © 2011-2022 走看看