• 省选测试33


    A matrix

    题目大意 : 问每个子矩阵本质不同行的个数和

    • 对于每行建出来trie树,每个节点记录下来有哪几行,在一个结点出现几行的前缀一定是一样的,算出贡献,

    • 然后将trie树根的叶子节点合并,相当于忽略一行,就是不看前面的行然后算前缀,合并的时候类似线段树合并,set合并的时候启发式一下就行

    • 每次暴力算复杂度不对,所以在更新每个节点的set的时候实时维护结果

    Code

    Show Code
    #include <set>
    #include <cstdio>
    #include <unordered_map>
    
    using namespace std;
    const int N = 5e5 + 5;
    
    int read(int x = 0, int f = 1, char c = getchar()) {
        for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
        for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
        return x * f;
    }
    
    set<int> s[N];
    int n, m, trc, rt;
    long long sum, ans;
    set<int>::iterator it;
    unordered_map<int, int> ch[N];
    
    void Add(int x, int w) {
        if (s[x].find(w) != s[x].end()) return;
        set<int>::iterator it = s[x].lower_bound(w);
        int lt = it == s[x].begin() ? 0 : (--it, *it++);
        ans += 1ll * (w - lt) * (n - w + 1);
        if (it != s[x].end()) ans -= 1ll * (w - lt) * (n - *it + 1);
        s[x].insert(w);
    }
    
    void Del(int x) {
        int lt = 0;
        for (it = s[x].begin(); it != s[x].end(); ++it)
            ans -= 1ll * (*it - lt) * (n - *it + 1), lt = *it;
    }
    
    void Merge(int &x, int y) {
        if (!x) return x = y, void();
        if (s[x].size() < s[y].size()) swap(x, y);
        for (it = s[y].begin(); it != s[y].end(); ++it) Add(x, *it);
        Del(y);
        for (auto c : ch[y]) Merge(ch[x][c.first], c.second);
    }
    
    int main() {
        n = read(); m = read();
        for (int i = 1; i <= n; ++i) {
            int x = 0;
            for (int j = 1; j <= m; ++j) {
                int c = read(), &y = ch[x][c];
                if (!y) y = ++trc;
                Add(y, i); x = y;
            }
        }
        while (m--) {
            Del(rt); sum += ans;
            int x = rt; rt = 0;
            for (auto y : ch[x]) 
                Merge(rt, y.second);
        }
        printf("%lld
    ", sum);
        return 0;
    }
    

    B sequence

    题目大意 : 问l到r中有几个区间的与和能被k整除

    • 其实挺简单

    • 枚举右端点i,建一颗线段树,每个叶子节点维护有多少个j为起点的区间的与和能被k整除

    • 对于右端点相同的区间,与和只有log种取值,能被k整除的就区间加一

    • 把询问离线下来,在询问l,r的时候,就在更新完右端点为r的时候查询l,r的区间和即可

    Code

    Show Code
    #include <cstdio>
    #include <vector>
    #include <algorithm>
    #define ls (rt << 1)
    #define rs (rt << 1 | 1)
    
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5;
    
    int read(int x = 0, int f = 1, char c = getchar()) {
        for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
        for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
        return x * f;
    }
    
    int n, m, k, a[N], p[32][N], c[32], s[32];
    ll t[N*4], tag[N*4], ans[N*5];
    vector<int> b[N], id[N];
    
    void Pushdown(int rt, int l, int r) {
        int mid = l + r >> 1; ll tmp = tag[rt]; tag[rt] = 0;
        tag[ls] += tmp; t[ls] += tmp * (mid - l + 1);
        tag[rs] += tmp; t[rs] += tmp * (r - mid);
    }
    
    void Add(int rt, int l, int r, int x, int y) {
        if (x <= l && r <= y) return t[rt] += (r - l + 1), tag[rt]++, void();
        if (tag[rt]) Pushdown(rt, l, r);
        int mid = l + r >> 1;
        if (x <= mid) Add(ls, l, mid, x, y);
        if (y > mid) Add(rs, mid+1, r, x, y);
        t[rt] = t[ls] + t[rs];
    }
    
    ll Ask(int rt, int l, int r, int x, int y) {
        if (x <= l && r <= y) return t[rt];
        if (tag[rt]) Pushdown(rt, l, r);
        int mid = l + r >> 1; ll ans = 0;
        if (x <= mid) ans = Ask(ls, l, mid, x, y);
        if (y > mid) ans += Ask(rs, mid+1, r, x, y);
        return ans;
    }
    
    int main() {
        n = read(), m = read(), k = read();
        for (int i = 1; i <= n; ++i)  {
            a[i] = read();
            for (int j = 0; j < 30; ++j)
                p[j][i] = (a[i] >> j & 1) ? p[j][i-1] : i;
        }
        for (int i = 1; i <= m; ++i) {
            int l = read(), r = read();
            b[r].push_back(l); id[r].push_back(i);
        }
        for (int i = 1; i <= n; ++i) {
            for (int j = 0; j < 30; ++j)
                s[j] = p[j][i];
            int sum = (1 << 30) - 1, lt = i; sort(s, s + 30);
            for (int j = 29; j >= 0 && s[j]; --j) {
                if (s[j] == s[j+1]) continue;
                if (sum % k == 0) Add(1, 1, n, s[j] + 1, lt);
                sum &= a[s[j]]; lt = s[j];
            }
            if (sum % k == 0) Add(1, 1, n, 1, lt);
            for (int j = 0; j < b[i].size(); ++j)
                ans[id[i][j]] = Ask(1, 1, n, b[i][j], i);
        }
        for (int i = 1; i <= m; ++i)
            printf("%lld
    ", ans[i]);
        return 0;
    }
    

    C permutation (Unaccepted)

    题目大意 :

    Code

    Show Code
  • 相关阅读:
    Hbase性能调优(一)
    文章标题
    JDBC的PreparedStatement启动事务使用批处理executeBatch()
    java.lang.OutOfMemoryError: PermGen space及其解决方法
    linux 关机命令总结
    oracle 启动关闭以及监听启动关闭命令
    bash: sqlplus: command not found 解决方法
    VMware 虚拟机 linux执行 ifconfig 命令 eth0没有IP地址(intet addr、Bcast、Mask) UP BROADCAST MULTICAST 问题
    Linux的文本编辑和文本内容查看命令
    RHEL6服务器网络配置 修改linux RHEL6系统下的ip方法
  • 原文地址:https://www.cnblogs.com/shawk/p/14462265.html
Copyright © 2020-2023  润新知