2021牛客暑期多校训练营9 E

好题-题面🎈

翻译翻译就是root为1的n个点的树,root点值最高,离root越远点值越小,现在m个询问,问从pos点出发,点值在【l,r】区间的点,最多能覆盖多少点

输入
4
1 2
1 3
2 4
10 8 7 6
3
1 10 10
2 6 7
3 7 10
输出
1 //只有1点
0 //2点值8,不符合6~7
3 //1 2 3点

思路🎈

以主城为根。离主城越远温度越低。首先可以根据病毒的温度区间[l,r]

可以找到病毒可以传播到离主城最近的城市是哪个。这个用倍增很好做。

找到这个点之后就是统计这个点为根的子树有多少温度在r以上。

dfs序做主席树,然后二分找比k值大于等于的个数

(比赛自己写的主席树写开花了,于是抄了个板子)

划分树ac代码

#include <bits/stdc++.h>
#include<complex>
#define ull unsigned long long
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=1e5+10;

struct Edge {
    int v, nxt;
} E[N*2];
int Head[N], erear;
void edge_init() {
    erear = 0;
    memset(Head, -1, sizeof(Head));
}
void edge_add(int u, int v) {
    E[erear].v = v;
    E[erear].nxt = Head[u];
    Head[u] = erear++;
}
int sorted[N*2],dfn=0,zz[N];
int L[N],R[N];
int fa[N][20];
void DFS(int u, int f) {
    sorted[++dfn]=zz[u];
    L[u]=dfn;
    for(int i = Head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if(v == f) continue;
        DFS(v, u);
        fa[v][0]=u;
    }
    sorted[++dfn]=zz[u];
    R[u]=dfn;
}

const int INF = 1e9;
const int MAXN = 2e5 + 5;
const int LOG_N = 30;
// tree[dep][i] 第dep层第i个位置的数值
int tree[LOG_N][MAXN];
// toleft[p][i] 第p层前i个数中有多少个整数分入下一层
int toleft[LOG_N][MAXN];
void build(int l, int r, int dep)
{
    if(l == r) return;
    int mid = (l + r) / 2;
    int same = mid - l + 1; // 和中点数相同的数的个数
    for(int i = l; i <= r; i++)
        if(tree[dep][i] < sorted[mid]) same--;
    int lpos = l, rpos = mid + 1;
    for(int i = l; i <= r; i++)
    {
        if(tree[dep][i] < sorted[mid])
            tree[dep + 1][lpos++] = tree[dep][i];
        else if(tree[dep][i] == sorted[mid] && same)
        {
            tree[dep + 1][lpos++] = tree[dep][i];
            same--;
        }
        else tree[dep + 1][rpos++] = tree[dep][i];
        toleft[dep][i] = toleft[dep][l - 1] + lpos - l;
    }
    build(l, mid, dep + 1);
    build(mid + 1, r, dep + 1);
}
// [L,R]里查询子区间[l,r]第k小的数
int query(int L1, int R1, int l, int r, int dep, int k)
{
    if(l == r) return tree[dep][l];
    int mid = L1 + R1>>1;
    // 有多少个查询区间内的节点会进入下一层的左子树
    int cnt = toleft[dep][r] - toleft[dep][l - 1];
    if(cnt >= k){
        int newl = L1 + toleft[dep][l - 1] - toleft[dep][L1 - 1];
        int newr = newl + cnt - 1;
        return query(L1, mid, newl, newr, dep + 1, k);
    }
    else{
        int newr = r + toleft[dep][R1] - toleft[dep][r];
        int newl = newr - (r - l - cnt);
        return query(mid + 1, R1, newl, newr, dep + 1, k - cnt);
    }
}
int solve(int s, int t, int h)
{
    int l = 1, r = (t - s) + 1, mid;
    int ans = 0;
    while(l <= r)
    {
        mid = (l + r) / 2;
        if(query(1, dfn, s, t, 0, mid) <= h)
        {
            ans = mid;
            l = mid + 1;
        }
        else r = mid - 1;
    }
    return ans;
}
int n;
int main(){

    edge_init();
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        edge_add(u,v);
        edge_add(v,u);
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&zz[i]);
    }
    DFS(1,0);
    for(int i=1;i<=dfn;i++){
        tree[0][i]=sorted[i];
    }
    sort(sorted+1,sorted+1+dfn);
    build(1,dfn,0);
    for (int i=1;i<20;i++){
        for (int j=1;j<= n;j++){
            fa[j][i] = fa[fa[j][i - 1]][i - 1];
        }
    }
    int m;scanf("%d",&m);
    while(m--){
       int u,mx,mi; scanf("%d%d%d",&u,&mi,&mx);
       if(zz[u]>mx || zz[u]<mi){
            printf("0\n");continue;
       }
       int p=19;
        while (p--){
            if (fa[u][p] == 0 || zz[fa[u][p]]>mx) continue;
            u=fa[u][p];
        }
       mi--;//这里是找小于mi的个数
       int l=L[u],r=R[u];
       int zhi=solve(l,r,mi);
       zhi=r-l+1-zhi;
       printf("%d\n",zhi/2);
    }
    return 0;
}
/*
4
1 2
1 3
2 4
10 8 7 6
3
1 10 10
2 6 7
3 7 10
*/

水哥的主席树orz

#pragma warning(suppress : 4996)
#define _CRT_SECURE_NO_WARNINGS
#include <list>
#include <iostream>
#include <map>
#include <fstream>
#include <algorithm>
using namespace std;

typedef  long long ll;
const int N = 1e5 + 5;
const ll mod = 1e9 + 7;
int n;

int g[N], edge[N * 2], nex[N * 2];
int lz = 1;
int fa[N][18];//倍增
int t[N];//温度
int tmp[N * 3 + 5];
int cid[N], tl[N], tr[N];
int R = 0;

int rt[N * 40], ls[N * 40], rs[N * 40], cnt[N * 40], tot;

void build(int& p, int l, int r)
{
    p = ++tot;
    cnt[p] = 0;
    if (l == r)return;
    int mid = (l + r) / 2;
    build(ls[p], l, mid);
    build(rs[p], mid + 1, r);
}
void update(int& p, int l, int r, int last, int x)
{
    p = ++tot;
    int mid = (l + r) >> 1;
    cnt[p] = cnt[last] + 1;
    //printf("%d %d\n",p,cnt[p]);

    ls[p] = ls[last];
    rs[p] = rs[last];
    if (l == r)return;
    if (x <= mid) update(ls[p], l, mid, ls[last], x);
    else update(rs[p], mid + 1, r, rs[last], x);
}
int  query(int x, int y, int l, int r, int h)
{
    if (l == r)return h <= l ? cnt[y] - cnt[x] : 0;
    int mid = (l + r) >> 1;
    if (mid + 1 < h)
    {
        return query(rs[x], rs[y], mid + 1, r, h);
    }
    else if (mid + 1 == h)
    {
        return cnt[rs[y]] - cnt[rs[x]];
    }
    else
    {
        return cnt[rs[y]] - cnt[rs[x]] + query(ls[x], ls[y], l, mid, h);
    }

}
void addEdge(int u, int v)
{
    edge[lz] = v;
    nex[lz] = g[u];
    g[u] = lz++;

    edge[lz] = u;
    nex[lz] = g[v];
    g[v] = lz++;
}
int dfsL[N], dfsR[N], dfstot;
void dfs(int u, int p)
{
    dfsL[u] = ++dfstot;
    update(rt[dfsL[u]], 0, R, rt[dfsL[u] - 1], t[u]);
    for (int i = g[u]; i != 0; i = nex[i])
    {
        int v = edge[i];
        if (v == p)continue;
        dfs(v, u);
        fa[v][0] = u;
    }
    dfsR[u] = dfstot;
}
int main()
{
    int u, v;
    int q;
    scanf("%d", &n);
    for (int i = 1; i < n; ++i)
    {
        scanf("%d%d", &u, &v);
        addEdge(u, v);
    }
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &t[i]);
        tmp[R++] = t[i];
    }
    scanf("%d", &q);
    for (int i = 0; i < q; ++i)
    {
        scanf("%d%d%d", &cid[i], &tl[i], &tr[i]);
        tmp[R++] = tl[i];
        tmp[R++] = tr[i];
    }
    sort(tmp, tmp + R);
    R = unique(tmp, tmp + R) - tmp;

    for (int i = 1; i <= n; ++i)
    {
        t[i] = lower_bound(tmp, tmp + R, t[i]) - tmp;
    }
    for (int i = 0; i < q; ++i)
    {
        tl[i] = lower_bound(tmp, tmp + R, tl[i]) - tmp;
        tr[i] = lower_bound(tmp, tmp + R, tr[i]) - tmp;
    }

    build(rt[0], 0, R);

    dfs(1, 0);
    for (int i = 1; i < 18; ++i)
    {
        for (int j = 1; j <= n; j++)
        {
            fa[j][i] = fa[fa[j][i - 1]][i - 1];
        }
    }
    for (int i = 0; i < q; ++i)
    {
        int u = cid[i];
        if (tl[i] > t[u] || tr[i] < t[u])
        {
            printf("0\n");
            continue;
        }
        int p = 18;
        while (p--)
        {
            if (fa[u][p] == 0 || t[fa[u][p]] > tr[i])continue;
            u = fa[u][p];
        }
        printf("%d\n", query(rt[dfsL[u] - 1], rt[dfsR[u]], 0, R, tl[i]));
    }
    return 0;
}

水哥两年不打,都比我这半退役的强啊

去年省赛E题

是一个求区间前k大之和,有一篇博客

主席树学习

posted @ 2021-08-17 12:18  ouluy  阅读(60)  评论(0编辑  收藏  举报