静态
学习博客
模板:区间第k小
带修改
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+5;
int n,m,cnt,root[maxn],a[maxn];
vector<int>p;
struct node{
int l,r,val;
}tr[maxn*4+maxn*17];//log(1e5)=17
int build(int l,int r){
int d=++cnt;
if(l==r)return d;
int mid=l+r>>1;
tr[d].l=build(l,mid);
tr[d].r=build(mid+1,r);
return d;
}
int updata(int pre,int l,int r,int x){
int now=++cnt;
tr[now]=tr[pre];
if(l==r){
tr[now].val++;
return now;
}
int mid=l+r>>1;
if(x<=mid)tr[now].l=updata(tr[pre].l,l,mid,x);
else tr[now].r=updata(tr[pre].r,mid+1,r,x);
tr[now].val=tr[tr[now].l].val+tr[tr[now].r].val;
return now;
}
int query(int last,int first,int l,int r,int k){//返回ans在p的编号 ,两颗树的权值对着减(you和zuo)
if(l==r)return r;
int mid=l+r>>1;
int val=tr[tr[last].l].val-tr[tr[first].l].val;
if(k<=val)return query(tr[last].l,tr[first].l,l,mid,k);
else return query(tr[last].r,tr[first].r,mid+1,r,k-val);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
p.push_back(a[i]);
}
sort(p.begin(),p.end());
p.erase(unique(p.begin(),p.end()),p.end());
int siz=p.size()-1;
//root[0]=build(0,siz);
for(int i=1;i<=n;i++){
int d=lower_bound(p.begin(),p.end(),a[i])-p.begin();
root[i]=updata(root[i-1],0,siz,d);
}
while(m--){
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",p[query(root[r],root[l-1],0,siz,k)]);
}
}
2021 ICPC 昆明 M Stone Games
题意:
在线询问,下标在
[
l
,
r
]
[l,r]
[l,r]内的数,所不能构成的最小的数是多少?
思路
先考虑给定n个数,如何计算无法凑出的最小数是多少:
1.先判断是否有1,如果有1则可以组成1,否则答案就是1.
2.假设现在能组成[1,x],设在[1,x+1]范围内的数的总和为sum,
那么就可以组成[1,sum],如果sum=x,说明x不能再增长了,答案为x+1,
否则令x=sum,继续求[1,x+1]范围内数的总和,不断递推.
查询[1,x]的和可以用权值线段树.
x的增长至少是斐波那契数列{1,2,3,5,....},因此次数不会太大,我估计是O(log).
查找区间 [ l , r ] [ l,r ] [l,r]内,权值在 [ 1 , x ] [1,x ] [1,x]范围内的值的和,再查找[1,x+1]的和,若等于[1,x],则不能再继续扩展
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
struct Tree
{
int ls,rs;
ll sum;
}tree[maxn*31];
int a[maxn],rt[maxn],sz;
void add(int l,int r,int p,int &q,int v)
{
q=++sz;
tree[q]=tree[p];
tree[q].sum+=v;
if(l==r) return ;
int mid = (l+r) >> 1;
if(v<=mid) add(l, mid, tree[p].ls, tree[q].ls, v);
else add(mid+1, r, tree[p].rs, tree[q].rs, v);
}
ll query(int l,int r,int p,int q,ll v)
{
if(l==r) return tree[q].sum-tree[p].sum;
int mid=(l+r) >> 1;
if(v<=mid)
return query(l, mid, tree[p].ls, tree[q].ls, v);
else
return query(mid+1, r, tree[p].rs, tree[q].rs, v)+tree[tree[q].ls].sum-tree[tree[p].ls].sum;
}
int main()
{
int n,q; scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
add(1, 1000000000, rt[i-1], rt[i], a[i]);
}
ll ans=0;
int l,r;
while (q--) {
scanf("%d%d",&l,&r);
l=(l+ans)%n+1;
r=(r+ans)%n+1;
if(l>r) swap(l, r);
ans=0;
while (true) {
ll new_ans = query(1, 1000000000, rt[l - 1], rt[r],ans+1);
if(new_ans==ans)
break;
ans=new_ans;
}
printf("%lld\n",++ans);
}
}
2019 徐州 H题 Yuuki and a problem
题意:
n个数,两种操作,一种是修改某个数,一种的求一段区间的数,他们设子集所能表示的数的 m e x mexmex
思路
带修改的区间查询
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define lowbit(x) x&-x
const int maxn=2e5+10;
const int N=200000;
int a[maxn],root[maxn],sz,n,q;
struct Tr{
int ls,rs;
ll sum;
}tr[maxn*100];
void insert(int& now,int l,int r,int val,int v){
if(!now) now=++sz;
tr[now].sum+=val;
if(l==r) return;
int mid=(l+r)>>1;
if(v<=mid) insert(tr[now].ls, l, mid, val, v);
else insert(tr[now].rs, mid+1, r, val, v);
}
ll query(int l,int r,int ql,int qr,int p){
if(!p) return 0;
if(ql<=l && qr>=r) return tr[p].sum;
ll ans=0;
int mid=(l+r)>>1;
if(ql<=mid) ans+=query(l, mid, ql, qr, tr[p].ls);
if(qr>mid) ans+=query(mid+1, r, ql, qr, tr[p].rs);
return ans;
}
void insert(int x,int p,int val){
for(int i=x;i<=N;i+=lowbit(i)) insert(root[i],1, N, val, p);
}
ll query(int l,int r,int L,int R){
ll ans=0;
for(int i=r;i;i-=lowbit(i))
ans += query(1, N, L, R, root[i]);
for(int i=l-1;i;i-=lowbit(i))
ans -= query(1, N, L, R, root[i]);
return ans;
}
int main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
insert(i, a[i], a[i]);
}
while (q--) {
int op,l,r; scanf("%d%d%d",&op,&l,&r);
if(op==1){
insert(l, a[l], -a[l]);
a[l]=r;
insert(l, a[l], a[l]);
}
else {
if(l>r) swap(l, r);
ll ans=0;
while (true) {
ll new_ans=query(l, r, 1, min(ans+1,(ll)N));
if(new_ans==ans) break;
ans=new_ans;
}
printf("%lld\n",ans+1);
}
}
}
区间[l,r]前k大的和
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
int rt[N],idx;
ll pre[N];
int a[N];
vector<int> num;
struct node{
int l,r;
int cnt;
ll sum;
ll d;
}tr[N*40];
int n;
void init(){
pre[1]=1;
for(ll i=2;i<=100005;i++)
pre[i]=pre[i-1]+i*i;
}
void build(int &p,int l,int r){
p=++idx;
if(l==r){
return ;
}
int mid=l+r>>1;
build(tr[p].l,l,mid);
build(tr[p].r,mid+1,r);
}
int find(int x){
return lower_bound(num.begin(),num.end(),x)-num.begin()+1;
}
void insert(int &p,int q,int l,int r,int pos,int cnt){
p=++idx,tr[p]=tr[q];
tr[p].cnt+=1,tr[p].sum+=cnt;
if(l==r){
tr[p].d=cnt;
return ;
}
int mid=l+r>>1;
if(pos<=mid)
insert(tr[p].l,tr[q].l,l,mid,pos,cnt);
else
insert(tr[p].r,tr[q].r,mid+1,r,pos,cnt);
}
ll query(int p,int q,int l,int r,int k){
if(l==r){
return tr[p].d*k;
}
int cnt=tr[tr[p].r].cnt-tr[tr[q].r].cnt;
int mid=l+r>>1;
if(k<=cnt){
return query(tr[p].r,tr[q].r,mid+1,r,k);
}
else{
return tr[tr[p].r].sum-tr[tr[q].r].sum+query(tr[p].l,tr[q].l,l,mid,k-cnt);
}
}
int main(){
//ios::sync_with_stdio(false);
int t;
cin>>t;
init();
while(t--){
cin>>n;
int i;
idx=0;
num.clear();
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
num.push_back(a[i]);
rt[i]=0;
}
build(rt[0],1,n);
sort(num.begin(),num.end());
num.erase(unique(num.begin(),num.end()),num.end());
for(i=1;i<=n;i++){
insert(rt[i],rt[i-1],1,n,find(a[i]),a[i]);
}
int l,r,k;
int q;
cin>>q;
while(q--){
scanf("%d%d%d",&l,&r,&k);
printf("%lld\n",pre[(r-l+1)]+query(rt[r],rt[l-1],1,n,k));
}
}
}