• 省选测试2


    省选测试2

    T1

    题目链接

    ​ 我们可以对每个物品的(a)从小到大排序, 对每个询问的(m)也从小到大排序(离线下来). 这样我们枚举每个小于等于(m)的物品就好了.

    ​ 假设当前枚举到了第(j)个物品, (f[k])表示前(j)个物品从中选出若干个的(c)恰好凑成(k)的那些物品里, 最小的(b)的最大值.

    ​ 转移的话就是背包, (f[k] = max(f[k], min(f[k-a[j].c],a[j].b))).注意到倒叙枚举.

    ​ 最后判断一个询问的(k)(f)值是否大于(m+s)就好了.

    #include <bits/stdc++.h>
    
    using namespace std;
    
    inline long long read() {
        long long s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
    
    const int N = 1005, M = 1e5 + 5, K = 1e6 + 5;
    int n, q_;
    int f[M], ans[K];
    struct object { 
        int a, b, c; 
        friend int operator < (const object &x, const object &y) {
            return x.a < y.a;
        }  
    } a[N];
    struct ques {
        int m, k, s, id;
        friend int operator < (const ques &a, const ques &b) {
            return a.m < b.m;
        }
    } q[K];
    
    int main() {
    
        freopen("a.in","r",stdin); freopen("a.out","w",stdout);
    
        n = read(); 
        for(int i = 1;i <= n; i++) 
            a[i].c = read(), a[i].a = read(), a[i].b = read();
        sort(a + 1, a + n + 1);
        q_ = read();
        for(int i = 1;i <= q_; i++)
            q[i].m = read(), q[i].k = read(), q[i].s = read(), q[i].id = i;
        sort(q + 1, q + q_ + 1);
        int j = 1;
        f[0] = 1e9 + 10;
        for(int i = 1;i <= q_; i++) {
            for(; a[j].a <= q[i].m && j <= n; j++) 
                for(int k = M - 5;k >= a[j].c; k--)
                    f[k] = max(f[k], min(f[k - a[j].c], a[j].b));
            if(f[q[i].k] > q[i].m + q[i].s) ans[q[i].id] = 1;
        }
        for(int i = 1;i <= q_; i++)
            printf("%s
    ", ans[i] ? "TAK" : "NIE"); 
    
        fclose(stdin); fclose(stdout);
        return 0;
    }
    
    /*
    5
    6 2 7
    5 4 9
    1 2 4
    2 5 8
    1 3 9
    5
    2 7 1
    2 7 2
    3 2 0
    5 7 2
    4 1 5
    */
    

    T2

    题目链接

    ​ 首先(O(n^2))的dp应该很好想 : (f[i] = min(f[j], max{h[j+1]...h[i]})(sum_i-sum_j<=L))

    (sum)代表前缀和, (f[i])代表前(i)个分成合法方式的最小高度.

    ​ 我们可以考虑用线段树优化一下.

    ​ 线段树维护的是(f[j]),以及(f[j] + max{h[j+1]...h[i]}), 我们分别用(Max,F)表示.

    ​ 假设当前要更新(f[i]),那么(h[i])会影响的应该是(lm[i])(i-1)这一段的(Max).(lm[i])代表的是(i)左边第一个小于等于(h[i])的位置.

    ​ 然后我们用(h[i])取更新区间([lm[i],i-1])(F)值, 也就是对于一个节点(o), 把它的(F)值更新为(Max[o]+h[i]).

    ​ 现在我们就可以更新(f[i])了, 我们用二分法找到最左边的(sum_i-sum_j <=L)(j)的位置, 然后查询区间([j,i-1])的最小的(F).

    ​ 最后我们需要把新的(f[i])放入到线段树中取, 也就是单点修改位置(i)处的(Max)(F)值.

    #include <bits/stdc++.h>
    
    #define int long long
    
    #define ls(o) (o << 1)
    #define rs(o) (o << 1 | 1)
    #define mid ((l + r) >> 1)
    
    using namespace std;
    
    inline long long read() {
        long long s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
    
    const int N = 1e5 + 5, inf = 1e9 + 10;
    const int Inf = 1e18;
    int n, L, top;
    int h[N], w[N], lm[N], sta[N], Max[N << 2], tag[N << 2];
    long long f[N << 2], sum[N];
    
    void up(int o) {
        Max[o] = min(Max[ls(o)], Max[rs(o)]);
        f[o] = min(f[ls(o)], f[rs(o)]);
    }
    
    void build(int o, int l, int r) {
        if(l == r) { f[o] = Inf; Max[o] = inf; return ; }
        build(ls(o), l, mid); build(rs(o), mid + 1, r);
        up(o);
    }
    
    void modify(int o, int k) {
        tag[o] = k;
        f[o] = Max[o] + k;
    }
    
    void down(int o) {
        if(tag[o]) modify(ls(o), tag[o]), modify(rs(o), tag[o]), tag[o] = 0;
    }
    
    void change_f(int o, int l, int r, int x, int val) {
        if(l == r) { f[o] = Max[o] = val; return ; }
        down(o);
        if(x <= mid) change_f(ls(o), l, mid, x, val);
        if(x > mid) change_f(rs(o), mid + 1, r, x, val);
        up(o);
    }
    
    void change_mx(int o, int l, int r, int x, int y, int val) {
        if(x <= l && y >= r) { modify(o, val); return ; }
        down(o);
        if(x <= mid) change_mx(ls(o), l, mid, x, y, val);
        if(y > mid) change_mx(rs(o), mid + 1, r, x, y, val);
        up(o);
    }
    
    long long query(int o, int l, int r, int x, int y) {
        if(x <= l && y >= r) return f[o];
        down(o);
        long long res = Inf;
        if(x <= mid) res = min(res, query(ls(o), l, mid, x, y));
        if(y > mid) res = min(res, query(rs(o), mid + 1, r, x, y));
        return res;
    }
    
    signed main() {
    
        n = read(); L = read();
        for(int i = 1;i <= n; i++) h[i] = read(), w[i] = read();
        for(int i = 1;i <= n; i++) sum[i] = sum[i - 1] + w[i];
        sta[++ top] = 0;
        for(int i = 1;i <= n; i++) {
            while(top && h[sta[top]] <= h[i]) top --;
            lm[i] = sta[top]; sta[++ top] = i; 
        }
        // for(int i = 1;i <= n; i++) cout << lm[i] << " "; cout << "
    ";
        build(1, 0, n); change_f(1, 0, n, 0, 0);
        long long res;
        for(int i = 1;i <= n; i++) {
            change_mx(1, 0, n, lm[i], i - 1, h[i]);
            int p = lower_bound(sum, sum + n + 1, sum[i] - L) - sum;
            res = query(1, 0, n, p, i - 1);
            change_f(1, 0, n, i, res);
        }
        printf("%lld
    ", res);
    
        return 0;
    }
    

    T3

    题目链接

    ​ 树上主席树 + 启发式合并.

    ​ 假设没有连边操作. 我们遍历这颗树, 让每个节点在它的父亲节点的基础上插入新节点. 对于一个查询(x, y), 我们可以先求出它们的(lca), 然后在主席树上这样查询区间内有多少个数 : (siz[x] + siz[y] - siz[lca] - siz[fa[lca]]).

    ​ 那有连边操作怎么办呢? 为了保证复杂度, 我们选择启发式合并, 也就是把小的树合并到大的树上面. 用并查集维护一下每一个联通块的大小, 每次把小的那部分合并上去后就重新遍历一遍, 求出新的倍增数组(为了求(lca)).

    #include <bits/stdc++.h>
    
    #define ls(o) t[o].ls
    #define rs(o) t[o].rs
    #define mid ((l + r) >> 1)
    
    using namespace std;
    
    inline long long read() {
        long long s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
    
    const int N = 8e4 + 5, M = 5e7;
    int n, m, t_, tot, cnt, cnt_b;
    int b[N], rt[N], fa[N], siz[N], val[N], dep[N], head[N], f[N][21];
    struct tree { int ls, rs, siz; } t[M];
    struct edge { int to, nxt; } e[N << 1];
    
    int find(int x) {
        return fa[x] == x ? x : fa[x] = find(fa[x]);
    }
    
    void add(int x, int y) {
        e[++ cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y;
    }
    
    void build(int &o, int l, int r) {
        o = ++ tot;
        if(l == r) return ;
        build(ls(o), l, mid); build(rs(o), mid + 1, r);
    }
    
    int LCA(int x, int y) {
        if(dep[x] < dep[y]) swap(x, y);
        for(int i = 20;i >= 0; i--) if(dep[x] - dep[y] >= (1 << i)) x = f[x][i];
        if(x == y) return x;
        for(int i = 20;i >= 0; i--) if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
        return f[x][0];
    }
    
    void insert(int &o, int p, int l, int r, int x) {
        o = ++ tot;
        t[o] = t[p]; t[o].siz ++;
        if(l == r) return ;
        if(x <= mid) insert(ls(o), ls(p), l, mid, x);
        if(x > mid) insert(rs(o), rs(p), mid + 1, r, x);
    }
    
    int query(int a_, int b_, int c_, int d_, int l, int r, int k) {
        if(l == r) return l;
        int res = t[ls(a_)].siz + t[ls(b_)].siz - t[ls(c_)].siz - t[ls(d_)].siz;
        if(res >= k) return query(ls(a_), ls(b_), ls(c_), ls(d_), l, mid, k);
        else return query(rs(a_), rs(b_), rs(c_), rs(d_), mid + 1, r, k - res);
    }
    
    void get_tree(int x, int Fa) {
        dep[x] = dep[Fa] + 1; f[x][0] = Fa;
        insert(rt[x], rt[Fa], 1, cnt_b, val[x]);    
        for(int i = 1;i <= 20; i++) f[x][i] = f[f[x][i - 1]][i - 1];
        for(int i = head[x]; i ; i = e[i].nxt) {
            int y = e[i].to; if(y == Fa) continue ;
            get_tree(y, x);
        }
    }
    
    int main() {
    
        freopen("c.in","r",stdin); freopen("c.out","w",stdout);
    
        int testcase = read();
        n = read(); m = read(); t_ = read();
        for(int i = 1;i <= n; i++) b[i] = val[i] = read();
        for(int i = 1;i <= n; i++) fa[i] = i, siz[i] = 1;
        sort(b + 1, b + n + 1);
        cnt_b = unique(b + 1, b + n + 1) - b - 1;
        for(int i = 1;i <= n; i++) val[i] = lower_bound(b + 1, b + cnt_b + 1, val[i]) - b;
        for(int i = 1, x, y;i <= m; i++) {
            x = read(); y = read();
            if(x == y) continue ;
            add(x, y); add(y, x);
            x = find(x); y = find(y);
            if(x == y) continue ;
            if(siz[x] > siz[y]) swap(x, y);
            fa[x] = y; siz[y] += siz[x];
        }
        build(rt[0], 1, cnt_b);
        for(int i = 1;i <= n; i++) if(!dep[i]) get_tree(i, 0);
        int las = 0;
        for(int i = 1, x, y, k;i <= t_; i++) {
            char ch_; cin >> ch_;
            if(ch_ == 'Q') {
                x = read() ^ las; y = read() ^ las; k = read() ^ las;
                int lca = LCA(x, y);
                // cout << x << " " << y << " " << lca << " " << f[lca][0] << "!!!
    ";
                las = b[query(rt[x], rt[y], rt[lca], rt[f[lca][0]], 1, cnt_b, k)];
                printf("%d
    ", las);
            }
            if(ch_ == 'L') {
                x = read() ^ las; y = read() ^ las;
                int t1 = x, t2 = y;
                add(t1, t2); add(t2, t1);
                x = find(x), y = find(y);
                if(siz[x] > siz[y]) swap(x, y), swap(t1, t2);
                fa[x] = y; siz[y] += siz[x];
                get_tree(t1, t2);
            }
        }
    
        fclose(stdin); fclose(stdout);
    
        return 0;
    }
    
  • 相关阅读:
    centos7 关闭firewall安装iptables并配置
    Dubbo与Zookeeper、SpringMVC整合和使用(负载均衡、容错)
    URL地址下载图片到本地
    IDEA常用快捷键
    电商的支付前、中、后这3个流程都是怎么设计的?
    jenkins的部署
    mysql 授权用户 主从和备份
    windows下利用iis建立网站网站并实现局域共享
    nginx反向代理 和部分优化
    LNMP的搭建 及地址转换
  • 原文地址:https://www.cnblogs.com/czhui666/p/14465441.html
Copyright © 2020-2023  润新知