【10.10校内测试】【线段树维护第k小+删除】【lca+主席树维护前驱后驱】

贪心思想。将a排序后,对于每一个a,找到对应的删除m个后最小的b,每次更新答案即可。

如何删除才是合法并且最优的?首先,对于排了序的a,第$i$个那么之前就应该删除前$i-1$个a对应的b。剩下$m-i+1$可以删,那么在剩下的b中查找第$m-i+2$小即可。每次做完就删除当前a对应的b。

注意离散化。

还有数组不要开大了....

#include<bits/stdc++.h>
#define LL long long
using namespace std;

void read(int &x) {
    x = 0; int t = 1; char ch = getchar();
    while(ch > ‘9‘ || ch < ‘0‘)    { if(ch == ‘-‘)    t = -1; ch = getchar(); }
    while(ch >= ‘0‘ && ch <= ‘9‘) {
        x = x * 10 + ch - ‘0‘;
        ch = getchar();
    }
    x = x * t;
}

int a[500005], b[500005], n, m;

struct Node {
    int a, b;
    int ida, idb;
} mat[100005];
bool cmp(Node a, Node b) { if(a.a == b.a)    return a.b < b.b; return a.a < b.a; }

int siz[400005], val[400005];

void modify(int nd, int l, int r, int pos, int d) {
    if(l == r) {
        siz[nd] += d;
        return ;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid)    modify(nd << 1, l, mid, pos, d);
    else            modify(nd << 1 | 1, mid + 1, r, pos, d);
    siz[nd] = siz[nd << 1] + siz[nd << 1 | 1];
}

int query(int nd, int l, int r, int pos) {
    if(l == r)    return val[l];
    int mid = (l + r) >> 1;
    if(pos <= siz[nd << 1])    return query(nd << 1, l, mid, pos);
    else return query(nd << 1 | 1, mid + 1, r, pos - siz[nd << 1]);
}

int pos[100005];
void work() {
    sort(b + 1, b + 1 + n);
    int q = unique(b + 1, b + 1 + n) - b - 1;

    memset(siz, 0, sizeof(siz));
    sort(mat + 1, mat + 1 + n, cmp);
    for(int i = 1; i <= n; i ++) {
        pos[i] = lower_bound(b + 1, b + 1 + q, mat[i].b) - b;
        val[pos[i]] = mat[i].b;
        modify(1, 1, q, pos[i], 1);
    }

    LL ans = 0;
    int r = min(mat[1].b, query(1, 1, q, m + 1));
    ans = 1ll * mat[1].a * r;
    for(int i = 1; i <= m; i ++) {
        modify(1, 1, q, pos[i], -1);
        int res = m - i + 1;
        r = query(1, 1, q, res);
        if(mat[i + 1].b < r)    r = mat[i + 1].b;
        LL now = 1ll * mat[i + 1].a * r;
        ans = max(ans, now);
    }

    printf("%lld\n", ans);
}

int main() {
    freopen("d.in", "r", stdin);
    freopen("d.out", "w", stdout);
    int T;
    scanf("%d", &T);
    while(T --) {
        read(n); read(m);
        for(int i = 1; i <= n; i ++) {
            read(mat[i].a); read(mat[i].b);
            b[i] = mat[i].b;
        }
        work();
    }
    return 0;
}

依旧是数据结构。我们发现,对于一个点集,它们一定有一个lca,而满足包含所有点的最小点集就是所有点到这个lca的链上的所有点。再加上题目要求最小的$abs(a[u]-r)$,就是用每条链上r的前驱和后驱来更新答案。

用主席树维护即可。每个结点的版本是由它的父亲结点转移过来。这样做查询的时候直接取出当前结点和lca的父亲结点的版本即可。

关于这个前驱和后驱的计算可以学习一下。

#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int maxn = 1e9;

void read(int &x) {
    x = 0; int t = 1; char ch = getchar();
    while(ch > ‘9‘ || ch < ‘0‘)    { if(ch == ‘-‘)    t = -1; ch = getchar(); }
    while(ch >= ‘0‘ && ch <= ‘9‘) {
        x = x * 10 + ch - ‘0‘;
        ch = getchar();
    }
    x = x * t;
}

int n, q, type;

struct Node {
    int v, nex;
    Node(int v = 0, int nex = 0) :
        v(v), nex(nex) { }
} Edge[200005];

int h[100005], stot;
void add(int u, int v) {
    Edge[++stot] = Node(v, h[u]);
    h[u] = stot;
}

int sum[32*100005], ls[32*100005], rs[32*100005], tail;
inline int newnode(int x) {
    sum[++tail] = sum[x];
    ls[tail] = ls[x]; rs[tail] = rs[x];
    return tail;
}

inline void update(int nd) {
    sum[nd] = sum[ls[nd]] + sum[rs[nd]];
}

int insert(int nd, int l, int r, LL pos) {
    nd = newnode(nd);
    sum[nd] ++;
    if(l == r)    return nd;
    int mid = (l + r) >> 1;
    if(pos <= mid)    ls[nd] = insert(ls[nd], l, mid, pos);
    else rs[nd] = insert(rs[nd], mid + 1, r, pos);
    update(nd);
    return nd;
}

int queryl(int nd1, int nd2, int l, int r, int pos) {
    if(sum[nd1] == sum[nd2])    return 0;///这个结点下面没有新的点
    if(l == r)    return l;
    int mid = (l + r) >> 1;
    if(pos <= mid)    return queryl(ls[nd1], ls[nd2], l, mid, pos);
    else {
        int tmp = queryl(rs[nd1], rs[nd2], mid + 1, r, pos);////尽量往靠近pos的地方走
        if(tmp)    return tmp;///如果走不到就尽量往mid走
        else return queryl(ls[nd1], ls[nd2], l, mid, mid);
    }
}

int queryr(int nd1, int nd2, int l, int r, int pos) {
    if(sum[nd1] == sum[nd2])    return 0;
    if(l == r)    return l;
    int mid = (l + r) >> 1;
    if(pos > mid)    return queryr(rs[nd1], rs[nd2], mid + 1, r, pos);
    else {
        int tmp = queryr(ls[nd1], ls[nd2], l, mid, pos);
        if(tmp)    return tmp;
        else return queryr(rs[nd1], rs[nd2], mid + 1, r, mid);
    }
}

int dep[100005], jum[100005][21];
int lca(int u, int v) {
    if(dep[u] < dep[v])    swap(u, v);
    int t = dep[u] - dep[v];
    for(int p = 0; t; t >>= 1, p ++)
        if(t & 1)    u = jum[u][p];
    if(u == v)    return u;
    for(int p = 19; p >= 0; p --)
        if(jum[u][p] != jum[v][p])    u = jum[u][p], v = jum[v][p];
    return jum[u][0];
}

int rt[100005], a[100005];
void dfs(int u, int f) {
    jum[u][0] = f;
    for(int i = 1; i < 20; i ++)
        jum[u][i] = jum[jum[u][i - 1]][i - 1];
    dep[u] = dep[f] + 1;
    rt[u] = insert(rt[f], 1, maxn, a[u]);
    for(int i = h[u]; i; i = Edge[i].nex) {
        int v = Edge[i].v;
        if(v == f)    continue;
        dfs(v, u);
    }
}

int x[100005], p[100005];
int main() {
    freopen("e.in", "r", stdin);
    freopen("e.out", "w", stdout);
    scanf("%d%d%d", &n, &q, &type);
    for(int i = 1; i <= n; i ++)    read(a[i]);
    for(int i = 1; i < n; i ++) {
        int u, v;
        read(u); read(v);
        add(u, v); add(v, u);
    }
    dfs(1, 0);
    int lastans = 0;
    for(int i = 1; i <= q; i ++) {
        int r, k;
        scanf("%d%d", &r, &k);
        for(int j = 1; j <= k; j ++) {
            read(x[j]);
            p[j] = (x[j] - 1 + lastans * type) % n + 1;
        }
        int l = p[1];
        for(int j = 2; j <= k; j ++)
            l = lca(l, p[j]);
        l = jum[l][0];
        int res = maxn, tmp;
        for(int j = 1; j <= k; j ++) {
            tmp = queryl(rt[l], rt[p[j]], 1, maxn, r);
            if(tmp && r - tmp < res)    res = r - tmp;
            tmp = queryr(rt[l], rt[p[j]], 1, maxn, r);
            if(tmp && tmp - r < res)    res = tmp - r;
        }
        printf("%d\n", res);
        lastans = res;
    }
    return 0;
}

原文地址:https://www.cnblogs.com/wans-caesar-02111007/p/9768554.html

时间: 2024-10-08 16:12:17

【10.10校内测试】【线段树维护第k小+删除】【lca+主席树维护前驱后驱】的相关文章

HDU6621 K-th Closest Distance 第 k 小绝对值(主席树(统计范围的数有多少个)+ 二分 || 权值线段树+二分)

题意:给一个数组,每次给 l ,r, p, k,问区间 [l, r] 的数与 p 作差的绝对值的第 k 小,这个绝对值是多少 分析:首先我们先分析单次查询怎么做: 题目给出的数据与多次查询已经在提示着我们在用数据结构去解决这个问题,对于常见的处理区间的数据结构首选线段树啦: 我觉得这道题的关键在于此:我们需要去二分答案ans,  为什么呢? 我们这样观察 ,对于 | p-a[i] | <= ans  等于 p-ans<=a[i] <=p+ans   那问题就转化为查询[L,R] 区间里面

COGS 930. [河南省队2012] 找第k小的数 主席树

主席树裸板子 #include<cstdio> #include<iostream> #include<algorithm> #define MAXN 100005 #define MAX 2000005 using namespace std; int sum[MAX],l[MAX],mid[MAX],r[MAX],a[MAXN],b[MAXN],n,m,sz,size,root[MAXN],cnt; void build(int &x,int z,int y

LCA+主席树 (求树上路径点权第k大)

SPOJ 10628. Count on a tree (树上第k大,LCA+主席树) 10628. Count on a tree Problem code: COT You are given a tree with N nodes.The tree nodes are numbered from 1 to N.Each node has an integer weight. We will ask you to perform the following operation: u v k 

BZOJ 1901 Zju 2112 Dynamic Rankings 动态维护第k小 树套树

题目大意:动态维护第k小. 思路:线段树套treap,裸题,就是不怎么好写. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 50010 #define INF 1e9 #define LEFT (pos << 1) #define RIGHT (pos << 1|1) #define SIZ

spoj COT - Count on a tree (树上第K小 LCA+主席树)

链接: https://www.spoj.com/problems/COT/en/ 思路: 首先看到求两点之前的第k小很容易想到用主席树去写,但是主席树处理的是线性结构,而这道题要求的是树形结构,我们可以用dfs跑出所有点离根的距离-dep[i](根为1,dep[1]也为1)在dfs的过程 中,我们对每一个节点建一棵线段树,那么[a,b]就是:root[a] + root[b] - root[lca(a,b)] - root[f[lca(a,b)]]; (因为a-b的路径上的权值还要算上lca(

bzoj 2588: Spoj 10628. Count on a tree LCA+主席树

2588: Spoj 10628. Count on a tree Time Limit: 12 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Input 第一行两个整数N,M. 第二行有N个整数,其中第i个整数

SPOJ 10628 Count on a tree (lca+主席树)

题意:给定一棵有n个结点的树,每一个点有一个权值.共同拥有m个询问.对于每一个询问(u,v,k),回答结点u至v之间第k小的点的权值. 思路:主席树+lca.首先指定一个根结点dfs一次并在此过程中建好主席树.对于对于每一个询问,我们仅仅须要考虑四棵树,即T[u], T[v], T[lca(u,v)], 再加上T[fa( lca(u,v) )],fa( lca(u,v) )表示lca(u, v)的父亲结点. 这样一来问题就和线性序列里第k小的数一样了. #include<cstdio> #in

SPOJ 10628 Count on a tree(Tarjan离线LCA+主席树求树上第K小)

COT - Count on a tree #tree You are given a tree with N nodes.The tree nodes are numbered from 1 to N.Each node has an integer weight. We will ask you to perform the following operation: u v k : ask for the kth minimum weight on the path from node u 

UVALive - 7831 :ACM Tax (主席树求树路径上中位数:LCA+主席树)

题意:给定一棵带权树,Q次询问,每次询问路径上的中位数. 思路:中位数分边数奇偶考虑,当当边数为num=奇时,结果就算路径第num/2+1大,用主席树做即可... (做了几道比较难的主席树,都wa了...只有来刷刷水题,准备晚上的CF了) #include<bits/stdc++.h> using namespace std; const int maxn=1000010; int Laxt[maxn],Next[maxn],To[maxn],cost[maxn],cnt; int fa[50