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大之和,有一篇博客