Super Mario
题意:n个数,m次询问,每次询问(L,R,H),输出区间[L,R]中小于等于H的数有多少个。在求区间第k大的基础上,加上一个二分就行了。对区间[0, R-L+1]进行二分,第k大的数和H相比,寻找答案。
//还有一种较为简单的做法,将H转换为离散的数后,直接在相减后的线段树里进行区间查询即可。
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn = 1e5+5;
struct num{
int val, idx, ser;
}nums[maxn];
int n, m;
struct tree{
int left, right, val;
}tree[maxn*20];
int top;
int root[maxn];
int ser[maxn];
inline int build(int l, int r){
int tmp = ++top;
if (l==r){
tree[top].val = 0;
return tmp;
}
int mid = (l+r)>>1;
tree[tmp].left = build(l, mid);
tree[tmp].right = build(mid+1, r);
tree[tmp].val = 0;
return tmp;
}
inline int add(int src, int l, int r, int target_idx){
int tmp = ++top;
if (l==r){
tree[tmp].val = tree[src].val + 1;
return tmp;
}
int mid = (l+r)>>1;
if (mid >= target_idx){
tree[tmp].left = add(tree[src].left, l, mid, target_idx);
tree[tmp].right = tree[src].right;
}else{
tree[tmp].right = add(tree[src].right, mid+1, r, target_idx);
tree[tmp].left = tree[src].left;
}
tree[tmp].val = tree[tree[tmp].left].val + tree[tree[tmp].right].val;
return tmp;
}
inline int search(int rt1, int rt2, int l, int r, int k){
if (l==r){
return l;
}
int x = tree[tree[rt2].left].val - tree[tree[rt1].left].val;
int y = tree[tree[rt2].right].val - tree[tree[rt1].right].val;
int mid = (l+r)>>1;
if (x >= k) return search(tree[rt1].left, tree[rt2].left, l, mid, k);
return search(tree[rt1].right, tree[rt2].right, mid+1, r, k-x);
}
inline int read(){
int x=0; char ch = getchar();
while (ch<'0' || ch>'9') ch = getchar();
while (ch>='0' && ch<='9'){
x = x*10 + ch-'0';
ch = getchar();
}
return x;
}
inline bool cmp1(struct num a, struct num b){
if (a.val != b.val) return a.val < b.val;
return a.idx < b.idx;
}
inline bool cmp2(struct num a, struct num b){
return a.idx < b.idx;
}
int main(){
int t, L, R, H, k;
int l, r, mid;
int cas = 0;
t = read();
while (t--){
top = 0;
n = read(), m = read();
for (int i=1; i<=n; i++){
nums[i].val = read();
nums[i].idx = i;
}
sort (nums+1, nums+n+1, cmp1);
for (int i=1; i<=n; i++){
nums[i].ser = i;
ser[i] = nums[i].val;
}
sort (nums+1, nums+n+1, cmp2);
root[0] = build(1, n);
for (int i=1; i<=n; i++)
root[i] = add(root[i-1], 1, n, nums[i].ser);
printf ("Case %d:\n", ++cas);
for (int i=1; i<=m; i++){
L = read()+1, R = read()+1, H = read();
l = 0, r = R-L+1;
while (l<r){
mid = ((l+r)>>1) + ((l+r)&1);
int tmp = ser[search(root[L-1], root[R], 1, n, mid)];
if (tmp <= H) l = mid;
else r = mid-1;
}
printf ("%d\n", l);
}
}
return 0;
}
P2633 Count on a tree
主席树+LCA。对每个结点结点线段树,表示从该结点到根节点路径上所有结点的权值建立起来的线段树。
要求(u, v)路径上第k小值,只需求线段树s[u]+s[v]-s[LCA(u,v)]-s[fa[LCA(u,v)]]上的第k小值即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int n, m;
// 树部分的变量
int val[maxn], head[maxn], lg[maxn], fa[maxn], dp[maxn][27], high[maxn];
struct edge{
int from, to, next;
}e[maxn*2];
int tot;
//离散化
struct value{
int idx, val;
}value[maxn];
// 主席树部分的变量 注意:主席树的区间是建立在离散后的权值数量上的,这和树的结点个数n相同
struct tree{
int left, right, val;
}tree[maxn*30];
int top, cnt; //top表示tree的使用情况, cnt为当前已有的线段树版本
int root[maxn];
//
int mp[maxn]; //结点i对应的主席树版本
inline int build(int l, int r){
int tmp = ++top;
if (l==r){
tree[tmp].val = 0;
return tmp;
}
int mid = (l+r)>>1;
tree[tmp].left = build(l, mid), tree[tmp].right = build(mid+1, r);
tree[tmp].val = 0;
return tmp;
}
inline int add(int src, int l, int r, int target_idx){
int tmp = ++top;
if (l==r){
tree[tmp].val = tree[src].val + 1;
return tmp;
}
int mid = (l+r)>>1;
if (mid >= target_idx){
tree[tmp].left = add(tree[src].left, l, mid, target_idx);
tree[tmp].right = tree[src].right;
}else{
tree[tmp].right = add(tree[src].right, mid+1, r, target_idx);
tree[tmp].left = tree[src].left;
}
tree[tmp].val = tree[tree[tmp].left].val + tree[tree[tmp].right].val;
return tmp;
}
inline int search(int rt1, int rt2, int rt3, int rt4, int l, int r, int k){ // u + v - LCA(u, v) - fa[LCA(u, v)]
if (l==r) return l;
int x = tree[tree[rt1].left].val + tree[tree[rt2].left].val - tree[tree[rt3].left].val - tree[tree[rt4].left].val;
int y = tree[tree[rt1].right].val + tree[tree[rt2].right].val - tree[tree[rt3].right].val - tree[tree[rt4].right].val;
int mid = (l+r)>>1;
if (x>=k) return search(tree[rt1].left, tree[rt2].left, tree[rt3].left, tree[rt4].left, l, mid, k);
else return search(tree[rt1].right, tree[rt2].right, tree[rt3].right, tree[rt4].right, mid+1, r, k-x);
}
inline int LCA(int x, int y){
if (high[y] < high[x]) swap(x, y);
while (high[y] > high[x]) y = dp[y][lg[high[y]-high[x]]-1];
if (x==y) return x;
for (int k=lg[high[x]]-1; k>=0; k--) if (dp[x][k] != dp[y][k])
x = dp[x][k], y = dp[y][k];
return dp[x][0];
}
inline void dfs(int x, int y){
fa[x] = y;
high[x] = high[y] + 1;
root[++cnt] = add(root[mp[y]], 1, n, val[x]);
mp[x] = cnt;
for (int i=head[x]; i!=-1; i=e[i].next) if (!high[e[i].to])
dfs(e[i].to, x);
return;
}
inline void Add(int from, int to){
tot++;
e[tot].from = from, e[tot].to = to, e[tot].next = head[from];
head[from] = tot;
return;
}
inline void init(){
for (int i=1; i<=n; i++) lg[i] = lg[i-1] + (1<<lg[i-1]==i);
for (int i=1; i<=n; i++) dp[i][0] = fa[i];
for (int k=1; k<=lg[n]-1; k++) for (int i=1; i<=n; i++)
dp[i][k] = dp[dp[i][k-1]][k-1];
return;
}
inline int read(){
int x = 0; char ch = getchar();
while (ch<'0' || ch>'9') ch = getchar();
while (ch>='0' && ch<='9'){
x = x*10 + ch-'0';
ch = getchar();
}
return x;
}
inline bool cmp(struct value a, struct value b){
if (a.val != b.val)
return a.val < b.val;
return a.idx < b.idx;
}
int main(){
int from, to, last = 0, u, v, k;
n = read(), m = read();
for (int i=1; i<=n; i++){
value[i].val = read();
value[i].idx = i;
}
sort (value+1, value+n+1, cmp);
for (int i=1; i<=n; i++) val[value[i].idx] = i;
top = 0;
root[0] = build(1, n);
memset (head, -1, sizeof head); tot = 0;
for (int i=1; i<n; i++){
from = read(), to = read();
Add(from, to); Add(to, from);
}
dfs(1, 0);
init();
for (int i=1; i<=m; i++){
u = read()^last, v = read(), k = read();
int tmp = LCA(u, v);
last = value[search(root[mp[u]], root[mp[v]], root[mp[tmp]], root[mp[fa[tmp]]], 1, n, k)].val;
printf ("%d\n", last);
}
return 0;
}
小睿睿的兄弟
bfs序+主席树+二分
在bfs序上构建主席树,然后寻找区间使用二分。寻找kth-father使用倍增。
时间复杂度nlogn+mlog^{2}n
#include <cstdio>
#include <queue>
using namespace std;
const int maxn = 1e6+1;
struct edge{
int to, next;
}e[maxn*2];
int n, m;
int val[maxn], head[maxn], tot;
int lg[maxn], pow2[30], dp[maxn][22], depth[maxn];
int BFS[maxn], mp1[maxn];
struct tree{ //主席树数据池,left,right左右孩子,val该区间内的数值个数
int left, right, val;
}tree[maxn*25];
int root[maxn], top; //每个版本主席树在数据池中的下标 top数据池使用情况
inline int fath(int x, int k){
//k--;
while (k){
x = dp[x][lg[k]-1];
k -= pow2[lg[k]-1];
}
return x;
}
inline int src1(int l, int r, int k){
int t = fath(BFS[r], k), s = depth[BFS[r]], tmp = r;
int mid;
while (l<r){
mid = (l+r) >> 1;
if (depth[BFS[mid]] < s) l = mid+1;
else r = mid;
}
r = tmp;
while (l<r){
mid = (l+r) >> 1;
if (fath(BFS[mid], k)!=t) l = mid+1;
else r = mid;
}
return l;
}
inline int src2(int l, int r, int k){
int t = fath(BFS[l], k), s = depth[BFS[l]], tmp = l;
int mid;
while (l<r){
mid = ((l+r)>>1) + ((l+r)&1);
if (depth[BFS[mid]] > s) r = mid-1;
else l = mid;
}
l = tmp;
while (l<r){
mid = ((l+r)>>1) + ((l+r)&1);
if (fath(BFS[mid], k)!=t) r = mid-1;
else l = mid;
}
return l;
}
inline int add(int src, int l, int r, int target){
int tmp = ++top;
if (l==r){
tree[tmp].val = tree[src].val + 1;
return tmp;
}
int mid = (l+r)>>1;
if (target<=mid){
tree[tmp].left = add(tree[src].left, l, mid, target);
tree[tmp].right = tree[src].right;
}else{
tree[tmp].left = tree[src].left;
tree[tmp].right = add(tree[src].right, mid+1, r, target);
}
tree[tmp].val = tree[tree[tmp].left].val + tree[tree[tmp].right].val;
return tmp;
}
inline int search(int rt1, int rt2, int l, int r, int k){
if (k>tree[rt2].val-tree[rt1].val) return -1;
if (l==r) return l;
int x = tree[tree[rt2].left].val - tree[tree[rt1].left].val;
int y = tree[tree[rt2].right].val - tree[tree[rt1].right].val;
int mid = (l+r)>>1;
if (x>=k) return search(tree[rt1].left, tree[rt2].left, l, mid, k);
return search(tree[rt1].right, tree[rt2].right, mid+1, r, k-x);
}
inline void bfs(){
queue<int> q;
int cnt = 0, tmp;
q.push(1);
while (!q.empty()){
tmp = q.front(); q.pop();
BFS[++cnt] = tmp, mp1[tmp] = cnt;
for (int i=head[tmp]; i; i=e[i].next) if (!mp1[e[i].to])
q.push(e[i].to);
}
return;
}
inline void dfs(int u, int v){
dp[u][0] = v;
depth[u] = depth[v] + 1;
for (int i=head[u]; i; i=e[i].next) if (!depth[e[i].to])
dfs(e[i].to, u);
return;
}
void init(){
for (int i=1; i<=n; i++) lg[i] = lg[i-1] + (1<<lg[i-1]==i);
pow2[0] = 1;
for (int i=1; i<=28; i++) pow2[i] = 2*pow2[i-1];
dfs(1, 0);
for (int k=1; k<=lg[n]-1; k++) for (int i=1; i<=n; i++)
dp[i][k] = dp[dp[i][k-1]][k-1];
return;
}
inline void AddEdge(int u, int v){
tot++;
e[tot].to = v, e[tot].next = head[u];
head[u] = tot;
return;
}
inline int read(){
int x=0; char ch=getchar();
while (ch<'0' || ch>'9') ch=getchar();
while (ch>='0' && ch<='9'){
x = x*10 + ch-'0';
ch=getchar();
}
return x;
}
int main(){
int u, v, last = 0, x, k, l, r;
n = read(), m = read();
for (int i=1; i<=n; i++) val[i] = read();
for (int i=1; i<n; i++){
u = read(), v = read();
AddEdge(u, v); AddEdge(v, u);
}
init();
bfs();
tree[0].left = tree[0].right = tree[0].val = 0;
top = 0; root[0] = 0;
for (int i=1; i<=n; i++){
root[i] = add(root[i-1], 1, n, val[BFS[i]]);
}
for (int i=1; i<=m; i++){
x = (read()^last)%n+1, k = read();
if (k>depth[x]-1){
puts("?");
continue;
}
l = src1(1, mp1[x], k), r = src2(mp1[x], n, k);
int tmp = search(root[l-1], root[r], 1, n, k);
if (tmp==-1){
puts("?");
continue;
}
last = tmp;
printf ("%d\n", last);
}
return 0;
}
0 条评论