省选测试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\n", 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 << "\n";
    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\n", 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] << "!!!\n";
            las = b[query(rt[x], rt[y], rt[lca], rt[f[lca][0]], 1, cnt_b, k)];
            printf("%d\n", 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;
}
posted @ 2021-03-01 19:36  C锥  阅读(64)  评论(0编辑  收藏  举报