zoukankan      html  css  js  c++  java
  • CF1534F2 Falling Sand (Hard Version)

    对所有的 # 编号并构造图:若 (i) 被“影响”后能影响 (j),则对 (i)(j) 连边。对于同一列的点,可以只对相邻的点连边。

    先找出原图中的所有极大强连通分量并缩点。对于 Easy Version,答案即为缩点图中入度为 (0) 的点数。

    对于 Hard Version,若 (a_i > 0),将第 (i) 列的第 (a_i) 个点标记为关键点。我们需要选出最少的点,使得它们可以到达所有关键点。

    设关键点的集合为 (S),若存在 (u, v in S)(u) 可达 (v),那么将 (v)(S) 中删除。

    有结论:每个点可以覆盖的存在关键点的列形成连续区间。

    证明:不失一般性地,设从左到右存在关键点 (u, v, w in S)(u) 可达 (w),但不可达 (v)。那么 (u) 可经过 (v) 列中的某点 (i),然后到达 (w),因为 (v) 的关键点高于 (i)(否则可以到达 (v)),所以 (v) 可达 (w),故 (w otin S)

    那么通过一次 DAG 上 DP 求出所有关键点和每个点对应的区间,对区间左端点排序后贪心即可。

    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    #include <numeric>
    #include <vector>
    #include <cassert>
    using namespace std;
    
    #define LOG(f...) fprintf(stderr, f)
    #define all(cont) begin(cont), end(cont)
    
    using ll = long long;
    
    template<class T> void read(T &x) {
      char ch; x = 0;
      int f = 1;
      while (isspace(ch = getchar()));
      if (ch == '-') ch = getchar(), f = -1;
      do x = x * 10 + (ch - '0'); while(isdigit(ch = getchar()));
      x *= f;
    }
    template<class T, class ...A> void read(T &x, A&... args) { read(x); read(args...); }
    
    const int N = 400005;
    
    vector<int> pos[N];
    char s[N];
    int pre_id[N];
    int a[N];
    
    int dfn[N], low[N], scc[N], dfc = 0, stk[N], top = 0, scc_cnt;
    bool instk[N];
    vector<int> G[N], RG[N];
    int r_ind[N];
    bool key[N], vis[N];
    int key_id[N], key_cnt;
    
    struct range {
      int l, r;
      bool operator<(const range &r) const { return l < r.l; }
    } ran[N];
    
    void tarjan(int x) {
      dfn[x] = low[x] = ++dfc;
      stk[++top] = x; instk[x] = true;
      for (int v : G[x])
        if (!dfn[v]) tarjan(v), low[x] = min(low[x], low[v]);
        else if (instk[v]) low[x] = min(low[x], dfn[v]);
      if (low[x] == dfn[x]) {
        int tp;
        do {
          tp = stk[top--];
          instk[tp] = false;
          scc[tp] = scc_cnt;
        } while (tp != x);
        ++scc_cnt;
      }
    }
    
    void scc_dp() {
      vector<int> ord;
      for (int i = 0; i < scc_cnt; ++i)
        if (!r_ind[i]) ord.push_back(i);
      for (int i = 0; i < scc_cnt; ++i) {
        int x = ord[i];
        if (vis[x]) key[x] = false;
        if (key[x]) vis[x] = true;
        for (int v : RG[x]) {
          --r_ind[v];
          if (vis[x] || key[x]) vis[v] = true;
          if (!r_ind[v]) ord.push_back(v);
        }
      }
      for (int i = 0; i < scc_cnt; ++i)
        if (key[i]) key_id[i] = key_cnt++;
      for (auto i = rbegin(ord); i != rend(ord); ++i) {
        int x = *i;
        ran[x].l = key[x] ? key_id[x] : numeric_limits<int>::max();
        ran[x].r = key[x] ? key_id[x] : numeric_limits<int>::min();
        for (int v : RG[x]) {
          ran[x].l = min(ran[x].l, ran[v].l);
          ran[x].r = max(ran[x].r, ran[v].r);
        }
      }
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
      freopen("input.txt", "r", stdin);
      freopen("output.txt", "w", stdout);
    #endif
      int n, m;
      read(n, m);
      for (int i = 0; i < n; ++i) {
        scanf("%s", s);
        for (int j = 0; j < m; ++j)
          if (s[j] == '#') pos[j].push_back(n - 1 - i);
      }
      for (int i = 0; i < m; ++i)
        read(a[i]);
      for (int i = 0; i < m; ++i)
        reverse(all(pos[i]));
      pre_id[0] = 0;
      for (int i = 1; i <= m; ++i)
        pre_id[i] = pre_id[i - 1] + pos[i - 1].size();
      for (int i = 0; i < m; ++i)
        for (auto it = begin(pos[i]); it != end(pos[i]); ++it) {
          int id = pre_id[i] + (it - begin(pos[i]));
          if (it != begin(pos[i])) G[id].push_back(id - 1);
          if (next(it) != end(pos[i]) && it[1] == *it + 1) G[id].push_back(id + 1);
          if (i != 0) {
            auto t = upper_bound(all(pos[i - 1]), *it);
            if (t != begin(pos[i - 1])) G[id].push_back(pre_id[i - 1] - 1 + (t - begin(pos[i - 1])));
          }
          if (i + 1 != m) {
            auto t = upper_bound(all(pos[i + 1]), *it);
            if (t != begin(pos[i + 1])) G[id].push_back(pre_id[i + 1] - 1 + (t - begin(pos[i + 1])));
          }
        }
      int nc = pre_id[m];
      for (int i = 0; i < nc; ++i)
        if (!dfn[i]) tarjan(i);
      for (int i = 0; i < m; ++i)
        if (a[i])
          key[scc[pre_id[i] + a[i] - 1]] = true;
      for (int i = 0; i < nc; ++i)
        for (int v : G[i])
          if (scc[i] != scc[v]) RG[scc[i]].push_back(scc[v]), ++r_ind[scc[v]];
      scc_dp();
      sort(ran, ran + scc_cnt);
      int max_r = -1, nmax_r = 0, res = 0;
      for (int i = 0; i < scc_cnt && max_r + 1 < key_cnt; ++i) {
        nmax_r = max(nmax_r, ran[i].r);
        if (i + 1 == scc_cnt || ran[i + 1].l > max_r + 1)
          ++res, max_r = nmax_r;
      }
      printf("%d
    ", res);
      return 0;
    }
    
  • 相关阅读:
    十分钟开发一个调用Activity的PhoneGap插件
    Mac下MAMP初试体验
    探索Android中的Parcel机制(上)
    两个栈实现队列+两个队列实现栈----java
    php实现工厂模式
    Hibernate Criterion
    Android用户界面概览
    秒杀多线程第四篇 一个经典的多线程同步问题
    Java串口通信具体解释
    逗比之——程序猿装逼手冊1(0基础版)
  • 原文地址:https://www.cnblogs.com/RiverHamster/p/sol-cf1534f2.html
Copyright © 2011-2022 走看看