题目链接 kth number
主
席
树
主席树
主席树
主席树模板题
关于主席树没啥好讲的,其实就是动态开点+线段树
#pragma GCC optimize(2)
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
const int mod = 1e9+9;
int Case = 1;
int n, m, root[maxn], tot;
struct node{
int l, r;
int sum;
}tr[maxn<<5];
void pushup(int rt) {
tr[rt].sum = tr[tr[rt].l].sum + tr[tr[rt].r].sum;
}
void build(int &rt, int l, int r) {
rt = ++tot;
if(l == r) {tr[rt].sum = 0;return;}
int mid = (l + r)/2;
build(tr[rt].l, l, mid);
build(tr[rt].r, mid+1, r);
pushup(rt);
}
void insert(int &rt, int pr, int pos, int l, int r) {
rt = ++tot;
if(l == r) {
tr[rt].sum++;
return;
}
tr[rt] = tr[pr];
int mid = (l + r)/2;
if(pos <= mid) insert(tr[rt].l, tr[pr].l, pos, l, mid);
else insert(tr[rt].r, tr[pr].r, pos, mid+1, r);
pushup(rt);
}
vector<int>ve;
int cc[maxn];
int getid(int x) {
return lower_bound(ve.begin(), ve.end(), x)-ve.begin()+1;
}
int query(int lc, int rc, int k, int l, int r) {
if(l == r) return l;
int sum = tr[tr[rc].l].sum-tr[tr[lc].l].sum, mid = (l+r)/2;
if(sum >= k) return query(tr[lc].l, tr[rc].l, k, l, mid);
else return query(tr[lc].r, tr[rc].r, k-sum, mid+1, r);
}
void solve() {
tot = 0;
scanf("%d%d", &n, &m);
build(root[0], 1, n);
for(int i = 1; i <= n; i++) {
scanf("%d", &cc[i]);
ve.push_back(cc[i]);
}
sort(ve.begin(), ve.end());
ve.erase(unique(ve.begin(), ve.end()), ve.end());
int s = ve.size();
for(int i = 1; i <= n; i++) {
int id = getid(cc[i]);
insert(root[i], root[i-1], id, 1, s);
}
for(int i = 1; i <= m; i++) {
int l, r, k;scanf("%d%d%d", &l, &r, &k);
printf("%d\n", ve[query(root[l-1], root[r], k, 1, s)-1]);
}
return;
}
int main() {
while(Case--) {
solve();
}
return 0;
}
整
体
二
分
整体二分
整体二分
整体二分中提现了分治的思想,但不是算法的关键,关键在分治的过程中对权值二分。
对于小于mid的更新就用树状数组更新,并且对于已经可以确定范围的询问也进行划分。把小于mid的询问和更新分成一组,把大于mid的询问减去左半部分已知的询问和大于mid的询问放在一起。
if(cc[i].op) {如果是询问
int tmp=tree.query(cc[i].y)-tree.query(cc[i].x-1);
if(tmp>=cc[i].k) t1[++cnt1]=cc[i];
else cc[i].k-=tmp, t2[++cnt2]=cc[i];
}
else {如果是更新
if(cc[i].x<=mid) t1[++cnt1]=cc[i], tree.update(cc[i].pos, cc[i].y);
else t2[++cnt2]=cc[i];
}
直到递归到二分的权值上界等于下界的时候在这个范围内的所有询问的答案也必然是此时的权值。
f(l>r || L>R) return;
if(L==R)
{
for(int i=l;i<=r;i++) if(cc[i].op) res[cc[i].pos]=L;
return;
}
最后就是分组后分治继续二分
for(int i=1;i<=cnt1;i++) if(!t1[i].op) tree.update(t1[i].pos, -t1[i].y);
for(int i=1;i<=cnt1;i++) cc[l+i-1]=t1[i];
for(int i=1;i<=cnt2;i++) cc[l+cnt1+i-1]=t2[i];
cal(L, mid, l, l+cnt1-1);cal(mid+1, R, l+cnt1, r);
细节:
树状数组每次清零别用memset
权值的上下界和序列的指针变量别搞混了。。。
#pragma GCC optimize(2)
#include<cstdio>
using namespace std;
const int maxn = 5e5+5;
typedef long long ll;
const ll mod = 1e9+7;
int Case = 1;
int n, m;
struct node{
int x, y, k, pos, op;
}cc[maxn], t1[maxn], t2[maxn];
int res[maxn];
struct Tree{
int c[maxn], up;
int lowbit(int x) {
return (x&(-x));
}
void update(int pos, int val) {
for(int i = pos; i <= up; i += lowbit(i)) {
c[i] += val;
}
}
int query(int pos) {
int res = 0;
for(int i = pos; i >= 1; i -= lowbit(i)) {
res += c[i];
}
return res;
}
}tree;
void cal(int L, int R, int l, int r) {
if(l>r || L>R) return;
if(L==R)
{
for(int i=l;i<=r;i++) if(cc[i].op) res[cc[i].pos]=L;
return;
}
int mid=(L+R)>>1, cnt1=0, cnt2=0;
for(int i = l; i <= r; i++) {
if(cc[i].op) {
int tmp=tree.query(cc[i].y)-tree.query(cc[i].x-1);
if(tmp>=cc[i].k) t1[++cnt1]=cc[i];
else cc[i].k-=tmp, t2[++cnt2]=cc[i];
}
else {
if(cc[i].x<=mid) t1[++cnt1]=cc[i], tree.update(cc[i].pos, cc[i].y);
else t2[++cnt2]=cc[i];
}
}
for(int i=1;i<=cnt1;i++) if(!t1[i].op) tree.update(t1[i].pos, -t1[i].y);
for(int i=1;i<=cnt1;i++) cc[l+i-1]=t1[i];
for(int i=1;i<=cnt2;i++) cc[l+cnt1+i-1]=t2[i];
cal(L, mid, l, l+cnt1-1);cal(mid+1, R, l+cnt1, r);
}
void solve() {
scanf("%d%d", &n, &m);
tree.up = n;
int cnt = 0;
for(int i = 1; i <= n; i++) {
cnt++;
scanf("%d", &cc[cnt].x);
cc[cnt].y = 1;cc[cnt].k = 0;cc[cnt].pos = i;cc[cnt].op = 0;
}
for(int i = 1; i <= m; i++) {
cnt++;
scanf("%d%d%d", &cc[cnt].x, &cc[cnt].y, &cc[cnt].k);
cc[cnt].pos = i;cc[cnt].op = 1;
}
cal(-1e9, 1e9, 1, cnt);
for(int i = 1; i <= m; i++) printf("%d\n", res[i]);
return;
}
int main() {
//g++ -std=c++11 -o2 1.cpp -o f && ./f < in.txt
//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
//freopen("in.txt", "r", stdin);
//freopen("out.txt","w",stdout);
#endif
//scanf("%d", &Case);
while(Case--) {
solve();
}
return 0;
}