• Educational Codeforces Round 84 (Rated for Div. 2)


    传送门

    A. Sum of Odd Integers

    (k)个不同奇数和的最小值为(k^2),那么必须满足:

    • (k,n)同奇偶;
    • (k^2leq n)

    代码如下:

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2020/3/23 22:35:41
     */
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <cmath>
    #include <set>
    #include <map>
    #include <queue>
    #include <iomanip>
    #include <assert.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define pb push_back
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
      template <template<typename...> class T, typename t, typename... A> 
      void err(const T <t> &arg, const A&... args) {
      for (auto &v : arg) std::cout << v << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 1e5 + 5;
     
    void run() {
        int n, k; cin >> n >> k;
        int sq = 1;
        while(sq * sq <= n) ++sq;
        --sq;   
        if(k <= sq && k >= 2 - (n & 1)) {
            int r = sq - k + 1;
            int rr = n - sq * sq;
            if((r & 1) != (rr & 1)) cout << "YES" << '
    ';
            else cout << "NO" << '
    ';
        } else cout << "NO" << '
    ';
    }
     
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        int T; cin >> T;
        while(T--) run();
        return 0;
    }
    

    B. Princesses and Princes

    贪心。

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2020/3/23 22:56:10
     */
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <cmath>
    #include <set>
    #include <map>
    #include <queue>
    #include <iomanip>
    #include <assert.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define pb push_back
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
      template <template<typename...> class T, typename t, typename... A> 
      void err(const T <t> &arg, const A&... args) {
      for (auto &v : arg) std::cout << v << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 1e5 + 5;
     
    int n;
    bool chk[N];
     
    void run() {
        cin >> n;
        vector <vector <int>> a(n);
        for(int i = 0; i < n; i++) {
            int k; cin >> k;
            a[i].resize(k);
            for(int j = 0; j < k; j++) cin >> a[i][j];   
        }
        for(int i = 0; i <= n; i++) chk[i] = false;
        int cnt = 0;
        vector <int> r;
        for(int i = 0; i < n; i++) {
            bool f = false;
            for(int j = 0; j < sz(a[i]); j++) {
                if(!chk[a[i][j]]) {
                    f = true;
                    chk[a[i][j]] = true;
                    ++cnt;
                    break;
                }   
            }
            if(f == false) r.push_back(i);
        }
        if(cnt == n) cout << "OPTIMAL" << '
    ';
        else {
            cout << "IMPROVE" << '
    ';
            cout << r[0] + 1 << ' ';
            for(int i = 1; i <= n; i++) if(!chk[i]) {
                cout << i << '
    ';
                break;
            }   
        }
    }
     
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        int T; cin >> T;
        while(T--) run();
        return 0;
    }
    

    C. Game with Chips

    先走到左上角,然后依次遍历整个网格就行。

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2020/3/23 23:07:11
     */
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <cmath>
    #include <set>
    #include <map>
    #include <queue>
    #include <iomanip>
    #include <assert.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define pb push_back
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
      template <template<typename...> class T, typename t, typename... A> 
      void err(const T <t> &arg, const A&... args) {
      for (auto &v : arg) std::cout << v << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 1e5 + 5;
     
    int n, m, k;
     
    void run() {
        cin >> n >> m >> k;
        for(int i = 1; i <= k; i++) {
            int x, y; cin >> x >> y;   
        }
        for(int i = 1; i <= k; i++) {
            int x, y; cin >> x >> y;   
        }   
        string res = "";
        for(int i = 0; i < n - 1; i++) res += "U";
        for(int i = 0; i < m - 1; i++) res += "L";
        for(int i = 0; i < n; i++) {
            if(i & 1) {
                for(int j = 0; j < m - 1; j++) res += "L";
            } else {
                for(int j = 0; j < m - 1; j++) res += "R";
            }                 
            if(i < n - 1) res += "D";
        }
        cout << res.length() << '
    ';
        cout << res << '
    ';
    }
     
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        run();
        return 0;
    }
    

    D. Infinite Path

    题意:
    给出一个(1...n)的排列(p),第(i)个位置有颜色(c_i)
    定义无穷路径:(i,p[i],p[p[i]]...)都有相同的颜色。
    定义(p imes p=p[p[i]]),类似可以定义(p^k)
    现在问找到最小的一个(k,k>0),满足(p^k)至少存在一条无穷路径。

    思路:
    连边(i ightarrow p[i]),那么最终会有若干个环,(p^k)每个位置的值即是在环上每个点走(k)步到达的值。
    存在一条无穷路径即可以转化为:存在一个点走若干次,每次走(k)步,最终能回到出发点,且所有到达的点的颜色都相同。
    那么对于每个环根据其长度找到约数,然后单独计算即可。
    约数的个数应该是(O(n^{frac{1}{3}}))级别的,时间复杂度不超过(O(n^{frac{4}{3}}))

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2020/3/24 0:28:41
     */
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <cmath>
    #include <set>
    #include <map>
    #include <queue>
    #include <iomanip>
    #include <assert.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define pb push_back
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
      template <template<typename...> class T, typename t, typename... A> 
      void err(const T <t> &arg, const A&... args) {
      for (auto &v : arg) std::cout << v << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 2e5 + 5;
     
    int n;
    int p[N], pos[N], c[N];
    bool chk[N];
    vector <int> G[N];
    vector <int> v;
     
    void dfs(int u) {
        chk[u] = true;
        v.push_back(c[pos[u]]);
        for(auto to : G[u]) if(!chk[to]) dfs(to);
    }
     
    int solve(int k) {
        for(int i = 0; i < k; i++) {
            bool ok = true;
            for(int j = i + k; j < sz(v); j += k) {
                if(v[j - k] != v[j]) ok = false;
            }
            if(ok) return k;
        }
        return n;
    }
     
    void run() {
        cin >> n;
        for(int i = 1; i <= n; i++) G[i].clear(), chk[i] = false;
        for(int i = 1; i <= n; i++) {
            cin >> p[i]; pos[p[i]] = i;
            G[i].push_back(p[i]);
        }
        for(int i = 1; i <= n; i++) cin >> c[i];
        int ans = n;
        for(int i = 1; i <= n; i++) if(!chk[i]) {
            v.clear();
            dfs(i);   
            int k = sz(v);
            for(int j = 1; j * j <= k; j++) if(k % j == 0) {
                ans = min(ans, min(solve(j), solve(k / j)));
            }
        }
        cout << ans << '
    ';
    }
     
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        int T; cin >> T;
        while(T--) run();
        return 0;
    }
    

    E. Count The Blocks

    枚举每个(block)的长度为(1,2,cdots,n),然后依次计算答案。
    对于每个枚举的长度(x),分情况这一段左右两边是否为合法位置计算就行。
    详见代码:

    Code
    
    

    F. AND Segments

    题意:
    给出(n,k,m),其中有(m)个限制((l_i,r_i,x_i)),现在要回答序列(a)的个数且要满足以下条件:

    • (0leq a_i<2^k);
    • 对于(1leq ileq m)(a[l_i]&cdots &a[r_i]=x_i)

    思路:

    • 可以按位分别进行考虑。
    • 对于每一位,限制条件则为存在某些区间,这上面的数必须为(1);存在另一些区间,二进制位上至少有一个(0)
    • 显然我们可以想到一个(dp:dp_{i,j})表示考虑了前面(i)个位置,最后一次(0)出现在(j)位置,目前满足所有限制条件右端点不超过(i)的方案数。
    • 转移分情况考虑:
      • 若当前位为(0),那么(dp_{i,0}=sum_{j=0}^{i-1} dp_{i-1,j});
      • 若当前位为(1),因为我们要满足至少有一个是(0)的限制条件,考虑以(i)为右端点的限制区间,假设区间左端点最大值为(k),那么(dp_{i,j}=dp_{i-1,j},jgeq k)。如果此时从小的地方转移过来会出现连续的(1)不满足条件。
    • 以上(dp)的时间复杂度为(O(n^2)),通过线段树优化则可以达到(O(nlogn))。接下来考虑进一步优化。
    • (pre_i)表示以(i)为右端点的询问区间最大的左端点的值,且满足([pre_i,i])至少有一个(0)。那么有个限制条件即为(pre_igeq pre_{i-1})
    • 注意到(dp)转移式中,(dp_i)的值都是从(i-1)复用,但是当当前这一位为(1)([pre_{i-1},pre[i]-1])这段区间我们必须舍弃,然而对于每个(i)这个过程是单调的,所以用一个指针维护一下就行。

    后面(dp)的优化挺巧妙的,主要是基于状态之间的复用这一性质,(dp_i)的值都来自于(dp_{i-1})。所以才能根据后面转移位置的单调性来进行优化。
    代码就比较简单:

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2020/3/24 17:50:08
     */
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <cmath>
    #include <set>
    #include <map>
    #include <queue>
    #include <iomanip>
    #include <assert.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define pb push_back
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
      template <template<typename...> class T, typename t, typename... A> 
      void err(const T <t> &arg, const A&... args) {
      for (auto &v : arg) std::cout << v << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 5e5 + 5, MOD = 998244353 ;
     
    int n, k, m;
    int l[N], r[N], x[N];
    int pre[N], cnt[N];
    int dp[N];
     
    int solve(int bit) {
        dp[0] = 1;
        memset(pre, 0, sizeof(pre));
        memset(cnt, 0, sizeof(cnt));
        for(int i = 1; i <= m; i++) {
            if(x[i] >> bit & 1) ++cnt[l[i]], --cnt[r[i] + 1];   
            else pre[r[i]] = max(pre[r[i]], l[i]);
        }
        int s = 1, tail = 0;
        for(int i = 1; i <= n; i++) {
            cnt[i] += cnt[i - 1];
            if(cnt[i]) dp[i] = 0;
            else dp[i] = s;
            while(tail < pre[i]) {
                s -= dp[tail++];
                if(s < 0) s += MOD;    
            }
            s += dp[i]; s %= MOD;
        }
        return s;
    }
     
    void run() {
        cin >> n >> k >> m;
        for(int i = 1; i <= m; i++) {
            cin >> l[i] >> r[i] >> x[i];
        }
        int ans = 1;
        for(int bit = 0; bit < k; bit++) {
            ans = 1ll * ans * solve(bit) % MOD;
        }
        cout << ans << '
    ';
    }
     
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        run();
        return 0;
    }
    

    G. Letters and Question Marks

    题意:
    给出(k)个字符串(t_i),所有串长度之和不超过(1000),字符集大小为(14)。每个字符串有一个代价(c_i)
    然后给出一个串(s),长度不超过(4cdot 10^5),字符集大小为(14),某些位置可能是(?),这样的位置数也不超过(14)
    现在要给(?)位置安排两两不同的字符,问最后总的代价最大为多少。
    代价计算方法为:(displaystylesum_{i=1}^kF(S, t_i)cdot c_i),其中(F(S, t_i))(t_i)串在(S)中出现的次数。

    思路:

    • 考虑暴力的方法:(dp_{i,sta})表示前(i)个位置,目前确定了的状态为(sta)的最大代价,然后对串找子串进行计算。找子串这一过程可以用(AC)自动机来,时间复杂度为(O(n^2cdot 2^{14}cdot 14))
    • 接下来考虑优化:假设(s)只存在一个(?),那么我们枚举所有状态时在(AC)自动机上面来匹配,每次都会从头开始跑,显然会浪费大量的时间,那么就有一个优化思路:

    优化一: 记录每个连续的段在(AC)自动机上面的转移。
    因为每次遇到一个(?)我们会枚举此时所有可能的状态,那么我们需要知道从所有结点出发连续的段在(AC)自动机上面的转移。

    • 暴力(dp)的每次转移,我们都需要找到(AC)自动机上面的结点,也就是说其实记录(s)串的长度是没有必要的,我们更关心的是(AC)自动机上面的结点和状态。那么又有一个优化思路:

    优化二: (dp_{i,sta})表示当在(AC)自动机上面的(i)结点,目前状态为(sta)时的最大代价。这样总状态数为(O(1000cdot 2^{14}))

    如果想到这里基本上这个题就出来了,每次遇到(?)时就直接在(AC)自动机上暴力转移之前连续的段,并更新状态。
    什么时候我也能这么思考啊
    细节见代码:

    Code
    /*
     * Author:  heyuhhh
     * Created Time:  2020/3/25 8:58:08
     */
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <cmath>
    #include <set>
    #include <map>
    #include <queue>
    #include <iomanip>
    #include <assert.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define pb push_back
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
      template <template<typename...> class T, typename t, typename... A> 
      void err(const T <t> &arg, const A&... args) {
      for (auto &v : arg) std::cout << v << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 4e5 + 5, M = 1005, MAX = 14;
    queue <int> q;
    namespace ACAM{
        int sz;
        int ch[M][MAX];
        int cnt[M], fail[M], sum[M];
        int newnode() {
            memset(ch[++sz], 0, sizeof(ch[sz]));
            cnt[sz] = fail[sz] = 0;
            return sz;
        }
        void init() {
            sz = -1;
            newnode();
        }
        void insert(char *s, int val) {
            int u = 0;
            for(int i = 1; s[i]; i++) {
                int idx = s[i] - 'a';
                if(!ch[u][idx]) ch[u][idx] = newnode();
                u = ch[u][idx];
            }
            sum[u] += val;
        }
        void build() {
            while(!q.empty()) q.pop();
            for(int i = 0; i < MAX; i++) {
                if(ch[0][i]) q.push(ch[0][i]);
            }
            while(!q.empty()) {
                int cur = q.front(); q.pop();
                for(int i = 0; i < MAX; i++) {
                    if(ch[cur][i]) {
                        fail[ch[cur][i]] = ch[fail[cur]][i];
                        sum[ch[cur][i]] += sum[ch[fail[cur]][i]];
                        q.push(ch[cur][i]);
                    } else {
                        ch[cur][i] = ch[fail[cur]][i];
                    }
                }
            }
        }
    };
    using namespace ACAM;
    int k;
    char s[N], t[M];
    int go[M];
    ll dp[M][1 << MAX], c[M];
     
    void chkmax(ll &x, ll y) {
        x = max(x, y);
    }
     
    void run() {
        cin >> k;
        init();
        for(int i = 1; i <= k; i++) {
            int c;
            cin >> (t + 1) >> c;   
            insert(t, c);
        }
        build();
        for(int i = 0; i <= sz; i++) {
            for(int j = 0; j < 1 << MAX; j++) {
                dp[i][j] = -1e18;
            }   
        }
        dp[0][0] = 0;
        for(int i = 0; i <= sz; i++) go[i] = i, c[i] = 0;
        cin >> (s + 1);
        int cnt = 0;
        for(int i = 1; s[i]; i++) {
            if(s[i] == '?') {
                for(int st = 0; st < 1 << MAX; st++) {
                    if(__builtin_popcount(st) != cnt) continue;
                    for(int j = 0; j <= sz; j++) if(dp[j][st] > -1e18) {
                        for(int t = 0; t < MAX; t++) if(!(st >> t & 1)) {
                            chkmax(dp[ch[go[j]][t]][st | (1 << t)], dp[j][st] + c[j] + sum[ch[go[j]][t]]);
                        }
                    }
                }
                for(int j = 0; j <= sz; j++) go[j] = j, c[j] = 0;
                ++cnt;
            } else {
                for(int j = 0; j <= sz; j++) go[j] = ch[go[j]][s[i] - 'a'], c[j] += sum[go[j]];
            }
        }
        ll ans = -1e18;
        for(int st = 0; st < 1 << MAX; st++) {
            if(__builtin_popcount(st) == cnt) {
                for(int i = 0; i <= sz; i++) {
                    chkmax(ans, dp[i][st] + c[i]);   
                }
            }   
        }
        cout << ans << '
    ';
    }
     
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        run();
        return 0;
    }
    
  • 相关阅读:
    CentOS 7 源码编译安装 Mysql 5.7
    Nginx 负载均衡 后端 监控检测 nginx_upstream_check_module 模块的使用
    cronolog 对 tomcat 7 进行日志切割
    OpenStack
    Oracle GoldenGate 异构平台同步(Mysql到Oracle)
    ELK 日志分析体系
    Tengine TCP 负载均衡
    MariaDB GTID 复制同步
    Mycat 安装配置
    Navicat破解
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/12564375.html
Copyright © 2020-2023  润新知