zoukankan      html  css  js  c++  java
  • 2019牛客多校 Round7

    Solved:5

    Rank:296

    E Find the median (线段树)

    题意:最开始一个空的数组 4e5次操作 每次把Li,Ri中的每个数插入进来 问当前的中位数

    题解:把这n个区间离散化去重以后 剩下m个点 可以分成m-1个连续的区间

       有个巧妙的方法是把所有的右端点+1后 每两个点之间表示一个左闭右开的区间

       然后线段树每个叶子节点就表示这个区间的信息 每次操作就是先区间更新再查询了

       用la表示这个区间被覆盖了多少次 查询的时候类似整体二分 其实还可以维护一个叶子节点表示的区间长度

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int MAXN = 4e5 + 5;
    
    int n, cnt;
    int a1, b1, c1, m1;
    int a2, b2, c2, m2;
    int x[MAXN], y[MAXN];
    int p[MAXN << 1], L[MAXN], R[MAXN];
    ll pre[MAXN << 1];
    
    ll sum[MAXN << 5];
    int la[MAXN << 5];
    int lz[MAXN << 5];
    void pushup(int rt) {
        sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
    }
    
    void pushdown(int l, int r, int rt) {
        if(lz[rt]) {
            lz[rt << 1] += lz[rt];
            lz[rt << 1 | 1] += lz[rt];
            la[rt << 1] += lz[rt];
            la[rt << 1 | 1] += lz[rt];
            int mid = l + r >> 1;
            sum[rt << 1] += 1LL * lz[rt] * (pre[mid] - pre[l - 1]);
            sum[rt << 1 | 1] += 1LL * lz[rt] * (pre[r] - pre[mid]);
            lz[rt] = 0;
        }
    }
    
    void update(int ql, int qr, int l, int r, int rt) {
        if(ql <= l && qr >= r) {
            lz[rt]++; la[rt]++;
            sum[rt] += pre[r] - pre[l - 1];
            return;
        }
        pushdown(l, r, rt);
    
        int mid = l + r >> 1;
        if(ql <= mid) update(ql, qr, l, mid, rt << 1);
        if(qr > mid) update(ql, qr, mid + 1, r, rt << 1 | 1);
        pushup(rt);
    }
    
    int query(int l, int r, int rt, ll k) {
        if(l == r) return p[l] + (k - 1) / la[rt];
        pushdown(l, r, rt);
    
        int mid = l + r >> 1;
        if(sum[rt << 1] >= k) return query(l, mid, rt << 1, k);
        else return query(mid + 1, r, rt << 1 | 1, k - sum[rt << 1]);
    }
    
    int main() {
        cnt = 0;
        scanf("%d", &n);
        scanf("%d%d%d%d%d%d", &x[1], &x[2], &a1, &b1, &c1, &m1);
        scanf("%d%d%d%d%d%d", &y[1], &y[2], &a2, &b2, &c2, &m2);
        for(int i = 3; i <= n; i++) {
            x[i] = (1LL * a1 * x[i - 1] + 1LL * b1 * x[i - 2] + 1LL * c1) % m1;
            y[i] = (1LL * a2 * y[i - 1] + 1LL * b2 * y[i - 2] + 1LL * c2) % m2;
        }
        for(int i = 1; i <= n; i++) {
            L[i] = min(x[i], y[i]) + 1;
            R[i] = max(x[i], y[i]) + 2;
            p[++cnt] = L[i], p[++cnt] = R[i];
        }
        sort(p + 1, p + 1 + cnt);
        cnt = unique(p + 1, p + 1 + cnt) - p - 1;
        p[cnt + 1] = p[cnt];
    
        for(int i = 1; i <= cnt; i++) pre[i] = pre[i - 1] + 1LL * (p[i + 1] - p[i]);
    
        ll tot = 0;
        for(int i = 1; i <= n; i++) {
            int t1 = lower_bound(p + 1, p + 1 + cnt, L[i]) - p;
            int t2 = lower_bound(p + 1, p + 1 + cnt, R[i]) - p - 1;
            update(t1, t2, 1, cnt, 1);
            tot += 1LL * (R[i] - L[i]); //左闭右开
            printf("%d
    ", query(1, cnt, 1, (tot + 1LL) / 2LL));
        }
        return 0;
    }
    E Find the median

    F Energy Stones 

    题意:n个石头 初始能量Ei 每秒能量增长Li 最多能量Ci

       有m次收割操作 每次选择一个时间点 将区间L,R内的石头能量都吸收 求最后一共吸收了多少能量

    题解:因为他是问的最后一共吸收了多少能量 不是问的每一次

       所以我们可以考虑每一个石头对答案产生了多少贡献

       对于每一个石头 这m次收割只有部分收割到了它 假设收割了k次

       如果我们知道了这k个时间点 除去第一个特判以外 每两个时间点之间会产出一次贡献

       贡献要么是Ci 要么是Li*区间长度

       对于n个石头 被收割的时间点有许多重复的 所以我们用set维护收割的时间点

       在枚举到到第L个石头的时候把时间点插入进set 在枚举到底R + 1个石头的时候 删除这个时间点

       然后一个树状数组维护长度为i的区间个数 一个树状数组维护长度为i的区间 长度和

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int MAXN = 1e5 + 5;
    
    int n, m;
    int E[MAXN], L[MAXN], C[MAXN];
    ll a[MAXN << 1], b[MAXN << 1];
    vector<int> g[MAXN];
    set<int> st;
    
    ll query1(int x) {
        ll res = 0;
        for(int i = x; i; i -= (i & -i)) res += a[i];
        return res;
    }
    
    ll query2(int x) {
        ll res = 0;
        for(int i = x; i; i -= (i & -i)) res += b[i];
        return res;
    }
    
    void add(int x, int val) {
        for(int i = x; i <= 200001; i += (i & -i)) {
            if(val > 0) a[i]++;
            else a[i]--;
            b[i] += 1LL * val;
        }
    }
    
    void inser(int x) {
        if(st.empty()) {
            st.insert(x);
            return;
        }
    
        auto pos = st.lower_bound(x);
        if(pos == st.begin()) {
            int t = (*pos) - x;
            add(t, 1LL * t);
        } else if(pos == st.end()) {
            int t = x - (*(prev(pos)));
            add(t, 1LL * t);
        } else {
            int t1 = x - (*(prev(pos)));
            int t2 = (*pos) - x;
            add(t1, 1LL * t1); add(t2, 1LL * t2);
            add(t1 + t2, 1LL * (-t1 - t2));
        }
        st.insert(x);
    }
    
    void del(int x) {
        auto pos = st.find(x);
        if(st.size() == 1) {
            st.erase(pos);
            return;
        }
        if(pos == st.begin()) {
            int t = (*(next(pos))) - x;
            add(t, 1LL * -t);
        } else if(pos == prev(st.end())) {
            int t = x - (*(prev(pos)));
            add(t, 1LL * -t);
        } else {
            int t1 = x - (*(prev(pos)));
            int t2 = (*(next(pos))) - x;
            add(t1, 1LL * -t1); add(t2, 1LL * -t2);
            add(t1 + t2, 1LL * (t1 + t2));
        }
        st.erase(pos);
    }
    
    int main() {
        int T;
        scanf("%d", &T);
        int cas = 0;
        while(T--) {
            st.clear();
            ll ans = 0;
            scanf("%d", &n);
            for(int i = 1; i <= n; i++) scanf("%d%d%d", &E[i], &L[i], &C[i]);
            for(int i = 1; i <= n + 1; i++) g[i].clear();
            memset(a, 0, sizeof(a)); memset(b, 0, sizeof(b));
    
            scanf("%d", &m);
            for(int i = 1; i <= m; i++) {
                int t, l, r; scanf("%d%d%d", &t, &l, &r);
                g[l].push_back(t);
                g[r + 1].push_back(-t);
            }
    
            for(int i = 1; i <= n; i++) {
                for(int j = 0; j < g[i].size(); j++) {
                    if(g[i][j] > 0) inser(g[i][j]);
                    else del(-g[i][j]);
                }
    
                if(st.empty()) continue;
                ans += min(1LL * C[i], 1LL * E[i] + 1LL * (*st.begin()) * L[i]);
                if(L[i] == 0) continue;
                int len = C[i] / L[i];
                ans += 1LL * query2(len) * L[i];
                ans += 1LL * C[i] * (st.size() - query1(len) - 1);
            }
            printf("Case #%d: %lld
    ", ++cas, ans);
        }
        return 0;
    }
    F Energy stones

    H pair

    题意:给三个数a,b,c 求有多少对数满足 x & y > c || x ^ y < c x的范围在1-a y的范围在1-b;

    题解:第一次写这种两个范围的数位DP.. 需要存的状态有

       当前枚举到pos位 And=0表示x&y和c的大小不确定 1表示x&y已经大于c 2表示已经小于

       Xor同理 然后存一下当前枚举两个数卡到的边界

       因为数位dp是从0开始统计的 还多统计了0^y(y<c) 和x^0(x<c) 的答案  所以还要减去

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int a, b, c;
    ll dp[35][3][3][2][2];
    
    ll dfs(int pos, int And, int Xor, int sj1, int sj2) {
        if(pos == -1) {
            if(And == 1 || Xor == 1) return 1;
            else return 0;
        }
        if(dp[pos][And][Xor][sj1][sj2] != -1) return dp[pos][And][Xor][sj1][sj2];
    
        ll res = 0;
        int up1 = sj1 ? (a >> pos & 1) : 1;
        int up2 = sj2 ? (b >> pos & 1) : 1;
    
        for(int i = 0; i <= up1; i++)
        for(int j = 0; j <= up2; j++) {
            int tand = And;
            int txor = Xor;
            if(tand == 0) {
                if((i & j) > (c >> pos & 1)) tand = 1;
                else if((i & j) < (c >> pos & 1)) tand = 2;
            }
            if(txor == 0) {
                if((i ^ j) < (c >> pos & 1)) txor = 1;
                else if((i ^ j) > (c >> pos & 1)) txor = 2;
            }
            res += dfs(pos - 1, tand, txor, sj1 && (i == up1), sj2 && (j == up2));
        }
        dp[pos][And][Xor][sj1][sj2] = res;
        return res;
    }
    
    int main() {
        memset(dp, -1, sizeof(dp));
    
        int T;
        scanf("%d", &T);
        while(T--) {
            memset(dp, -1, sizeof(dp));
            scanf("%d%d%d", &a, &b, &c);
            printf("%lld
    ", dfs(31, 0, 0, 1, 1) - min(a, c - 1) - min(b, c - 1) - 1);
        }
        return 0;
    }
    H pair

    I Chessbord

    题意:给出n,m 问构造一个k*k的矩阵 矩阵每个元素不小于m

       且从每一行选一个数 且这k个数的属于不同的列 方案数

    题解:枚举矩阵大小k 再枚举选出的k个元素的和j 为了计算方便 这个j等于实际的和减去k*m 那么元素大小可为0

       我们发现合法的方案一定长这个样子 假定k为3

       a1+b1  a2+b1  a3+b1

       a1+b2  a2+b2  a3+b2

       a1+b3  a2+b3  a3+b3

       这样我们随便选出的和一定都等于a1+a2+a3+b1+b2+b3 这就是我们枚举的和j

       这个方案数就等于C(j+2*k-1,2*k-1) 因为元素可以为0 那么我们让这2k项每个都先+1 分配完了再-1

       等价于从j+2*k-1个空位中插2*k-1个板子 当然这样计算是有重复的 

       重复是因为假如让a1,a2,a3都加1 b1,b2,b3都减1 只要b都非负 这个方案是一样的 但是也计数了 

       所以我们就要减去b1,b2,b3都大于0的情况了 那么就等于让k项都+1 分配完了再-1

       综上所述 一次枚举的方案数=C(j+2*k-1,2*k-1) - C(j+k-1,2*k-1)

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll mod = 998244353;
    ll jc[5005], ny[5005];
    ll n, m;
    
    ll pow_mod(ll x, ll y) {
        ll res = 1;
        while(y) {
            if(y & 1) res = res * x % mod;
            x = x * x % mod;
            y >>= 1;
        }
        return res;
    }
    
    ll C(ll x, ll y) {
        if(x < 0 || y < 0 || x < y) return 0;
        return jc[x] * ny[y] % mod * ny[x - y] % mod;
    }
    
    int main() {
        jc[0] = 1LL;
        for(ll i = 1; i <= 5000; i++) jc[i] = jc[i - 1] * i % mod;
        ny[5000] = pow_mod(jc[5000], mod - 2);
        for(ll i = 4999; i >= 0; i--) ny[i] = ny[i + 1] * (i + 1) % mod;
    
        int T;
        scanf("%d", &T);
        while(T--) {
            scanf("%lld%lld", &n, &m);
            ll ans = 0;
            for(ll i = 1; i <= n; i++) {
                if(i * m > n) break;
                for(ll j = 0; j <= n; j++) {
                    if(i * m + j > n) break;
                    ans += C(j + 2 * i - 1, 2 * i - 1) - C(j + i - 1, 2 * i - 1);
                    ans %= mod;
                }
            }
            printf("%lld
    ", (ans + mod) % mod);
        }
        return 0;
    }
    I Chessbord
  • 相关阅读:
    PHP编程资源
    JSP+Java编程资源
    Word、Excel办公书的资源下载
    听你说
    一些好看的渐变色(配色)网站推荐
    js判断数组中是否包含某个元素
    你才是你故事的作者
    vue-color 颜色选择器插件用法介绍
    vue-cli3 导入.md文件,vue中markdown文件的解析与渲染
    新版 animate.css 在vue中的正确使用
  • 原文地址:https://www.cnblogs.com/lwqq3/p/11363992.html
Copyright © 2011-2022 走看看