洛谷 P2617 Dynamic Rankings || ZOJ - 2112
写的让人看不懂,仅留作笔记
静态主席树,相当于前缀和套(可持久化方法构建的)值域线段树。
建树方法:记录前缀和的各位置的线段树的root。先建一个"第0棵线段树",是完整的(不需要用可持久化的方法),所有数据为0。后面每一个位置的前缀和放的线段树都先设root与前一位置的线段树一样,然后再按照原序列在指定位置进行(可持久化的)单点加法。(类比放数值的前缀和,每一位置的前缀和是前一位置前缀和加上当前位置数值)
带修改主席树,相当于区间树状数组套(可持久化方法构建的)值域线段树。
建树方法:记录树状数组各位置线段树的root。先建一个"第0棵线段树",是完整的,按普通线段树建,所有数据为0。一开始设所有root都为第0棵线段树的根。对于原序列某位置的值,相当于在树状数组某位置进行一次单点加法。
可持久化方法建线段树:每一个节点进行操作时,都先把原来的节点复制一份(指同时复制左右孩子(以前错在只复制了一个孩子)的位置和数据,但不递归复制左右孩子),再进行更新等操作。这样的话每一次单点更新操作只会新建logn个节点(因为每次单点更新只会路过这么多点),(相比一般的树套树:外层树每一个点放一个完整的内层树)可以节省空间。
由于是值域线段树,都需要做离散化。
这么做,就可以:在log^2n的时间内,完成一次查询"某区间内小于等于某数的数的个数"的操作。(对于区间[l,r],树状数组查询出[1,r]中答案和[1,l-1]中答案,然后相减)
区间第k小:
1.二分答案,log^3n
#include<cstdio>
#include<algorithm>
#include<tr1/unordered_map>
#define lowbit(x) ((x)&(-x))
#define mid ((l+r)>>1)
using namespace std;
using namespace tr1;
unordered_map<int,int> ma;
int ma2[];
struct Q
{
int t,i,j,x;
}q[];
int n,m,totn,a[],t[];
char tmp[];
int mem,root[];
int L,x;
int lc[],rc[],dat[];
void build(int l,int r,int& num)
{
num=mem++;
if(l==r){/*dat[num]=0;*/return;}
build(l,mid,lc[num]);
build(mid+,r,rc[num]);
}
void addx(int l,int r,int& num)
{
int t=num;num=mem++;
lc[num]=lc[t];rc[num]=rc[t];
if(l==r)
{
dat[num]=dat[t]+x;
return;
}
if(L<=mid) addx(l,mid,lc[num]);
else addx(mid+,r,rc[num]);
dat[num]=dat[lc[num]]+dat[rc[num]];
}
int query(int l,int r,int num)//返回该棵线段树中[1,x]的和,即小于等于x的数的个数
{
if(l==r) return dat[num];
if(x<=mid) return query(l,mid,lc[num]);
else return dat[lc[num]]+query(mid+,r,rc[num]);
}
void add(int x){while(x<=n){addx(,totn,root[x]);x+=lowbit(x);}}
int sum1(int x){int ans=;while(x>){ans+=query(,totn,root[x]);x-=lowbit(x);}return ans;}
int sum(int l,int r){return sum1(r)-sum1(l-);}
//返回给定x的情况下,[l,r]内小于等于x的数的个数
int main()
{
int i,l,r;
scanf("%d%d",&n,&m);
for(i=;i<=n;i++)
{
scanf("%d",&a[i]);
t[++t[]]=a[i];
}
for(i=;i<=m;i++)
{
scanf("%s",tmp);
if(tmp[]=='Q')
{
scanf("%d%d%d",&q[i].i,&q[i].j,&q[i].x);
//q[i].t=0;
}
else if(tmp[]=='C')
{
scanf("%d%d",&q[i].i,&q[i].x);
q[i].t=;
t[++t[]]=q[i].x;
}
}
sort(t+,t+t[]+);
//for(i=1;i<=t[0];i++) printf("%d ",t[i]);puts("");
totn=unique(t+,t+t[]+)-t-;//printf("%da\n",totn);
for(i=;i<=totn;i++) ma[t[i]]=i,ma2[i]=t[i];
for(i=;i<=n;i++) a[i]=ma[a[i]];//printf("%d ",a[i]);puts("");
for(i=;i<=m;i++)
{
if(q[i].t==)
{
q[i].x=ma[q[i].x];
//sz[q[i].x]++;
}
}
build(,totn,root[]);
//for(i=1;i<=n;i++) root[i]=root[0];//dat[i]=dat[0];
for(i=;i<=n;i++) L=a[i],x=,add(i);
//x=3;printf("%db\n",sum1(4));
for(i=;i<=m;i++)
{
if(q[i].t==)
{
l=;r=totn;
while(r-l>)
{
x=(l+r)>>;
if(sum(q[i].i,q[i].j)>=q[i].x) r=x;
else l=x;
}
printf("%d\n",ma2[r]);
}
else if(q[i].t==)
{
l=;r=totn;
while(r-l>)
{
x=(l+r)>>;
if(sum(q[i].i,q[i].i)>=) r=x;
else l=x;
}
L=r;x=-;add(q[i].i);
L=q[i].x;x=;add(q[i].i);
}
}
return ;
}
2.直接在线段树上二分,log^2n。可以发现普通的在(值域)线段树上直接二分的做法这里并不能使用,因为外层树是树状数组而不是线段树。需要用(看起来比较奇怪(?)的)写法。
我的方法是:根据树状数组查询[l,r]中"小于等于某数的数的个数"的做法,先提取出与此区间相关的线段树的根节点。那么该区间内小于等于某数的树的个数,就由这些线段树中统计出的一部分的答案相加再减去一部分的答案。而这些线段树的结构是完全一致的,因此可以同步地让这些节点向左/右儿子走。
#include<cstdio>
#include<algorithm>
#include<tr1/unordered_map>
#define lowbit(x) ((x)&(-x))
#define mid ((l+r)>>1)
using namespace std;
using namespace tr1;
unordered_map<int,int> ma;
int ma2[];
struct Q
{
int t,i,j,x;
}q[];
int n,m,totn,a[],t[];
char tmp[];
int mem,root[];
int L,x;
int lc[],rc[],dat[];
void build(int l,int r,int& num)
{
num=mem++;
if(l==r){/*dat[num]=0;*/return;}
build(l,mid,lc[num]);
build(mid+,r,rc[num]);
}
void addx(int l,int r,int& num)
{
int t=num;num=mem++;
lc[num]=lc[t];rc[num]=rc[t];
if(l==r)
{
dat[num]=dat[t]+x;
return;
}
if(L<=mid) addx(l,mid,lc[num]);
else addx(mid+,r,rc[num]);
dat[num]=dat[lc[num]]+dat[rc[num]];
}
void add(int x){while(x<=n){addx(,totn,root[x]);x+=lowbit(x);}}
int tmpr[][];
int query(int L,int R,int k)//返回[L,R]内第k小的数
{
int tx,l=,r=totn,now,i;
for(tx=R,tmpr[][]=;tx>;tx-=lowbit(tx)) tmpr[][++tmpr[][]]=root[tx];
for(tx=L-,tmpr[][]=;tx>;tx-=lowbit(tx)) tmpr[][++tmpr[][]]=root[tx];
while(l!=r)
{
now=;
for(i=;i<=tmpr[][];i++) now+=dat[lc[tmpr[][i]]];
for(i=;i<=tmpr[][];i++) now-=dat[lc[tmpr[][i]]];
if(now>=k)
{
r=mid;
for(i=;i<=tmpr[][];i++) tmpr[][i]=lc[tmpr[][i]];
for(i=;i<=tmpr[][];i++) tmpr[][i]=lc[tmpr[][i]];
}
else
{
k-=now;l=mid+;
for(i=;i<=tmpr[][];i++) tmpr[][i]=rc[tmpr[][i]];
for(i=;i<=tmpr[][];i++) tmpr[][i]=rc[tmpr[][i]];
}
}
return l;
}
int main()
{
int i,l,r;
scanf("%d%d",&n,&m);
for(i=;i<=n;i++)
{
scanf("%d",&a[i]);
t[++t[]]=a[i];
}
for(i=;i<=m;i++)
{
scanf("%s",tmp);
if(tmp[]=='Q')
{
scanf("%d%d%d",&q[i].i,&q[i].j,&q[i].x);
//q[i].t=0;
}
else if(tmp[]=='C')
{
scanf("%d%d",&q[i].i,&q[i].x);
q[i].t=;
t[++t[]]=q[i].x;
}
}
sort(t+,t+t[]+);
//for(i=1;i<=t[0];i++) printf("%d ",t[i]);puts("");
totn=unique(t+,t+t[]+)-t-;
for(i=;i<=totn;i++) ma[t[i]]=i,ma2[i]=t[i];
for(i=;i<=n;i++) a[i]=ma[a[i]];
for(i=;i<=m;i++)
{
if(q[i].t==)
{
q[i].x=ma[q[i].x];
//sz[q[i].x]++;
}
}
build(,totn,root[]);
//for(i=1;i<=n;i++) root[i]=root[0];//dat[i]=dat[0];
for(i=;i<=n;i++) L=a[i],x=,add(i);
for(i=;i<=m;i++)
{
//printf("%d\n",i);
if(q[i].t==)
{
printf("%d\n",ma2[query(q[i].i,q[i].j,q[i].x)]);
}
else if(q[i].t==)
{
L=query(q[i].i,q[i].i,);x=-;add(q[i].i);
L=q[i].x;x=;add(q[i].i);
}
}
return ;
}
修改:先用区间第k小找出这一个点组成的"区间"的"第1小",然后将这个值的数量减去1。然后再进行加的操作。
(要卡常的话,似乎也可以另开一个数组直接进行修改,然后查找"第1小"用在那个数组中查询代替)
以上代码中大量使用了引用的技巧,还是在别人的代码里看到的,很方便。另外好像(没试过)也可以写成使得函数返回当前节点修改完成后的副本。
如果是洛谷的那道题,这样已经够了。但是zoj那道的空间卡的更紧,而且原数列元素多,操作少,因此可以用一些其他技巧:一开始建一棵静态(前缀和)主席树,另建一棵空的动态主席树。修改就在动态主席树上修改,查询则结合两部分。
不过zoj的空间貌似有点奇怪...算出来lc、rc等应该是要开256万左右的,但是貌似会MLE...改成200万就过了
#include<cstdio>
#include<algorithm>
#include<map>
#define lowbit(x) ((x)&(-x))
#define mid ((l+r)>>1)
using namespace std;
map<int,int> ma;
int ma2[];
struct Q
{
int t,i,j,x;
}q[];
int n,m,totn,a[],t[];
char tmp[];
int mem,root[],root1[];
int L,x;
int lc[],rc[],dat[];
void build(int l,int r,int& num)
{
num=mem++;
if(l==r){dat[num]=;return;}
build(l,mid,lc[num]);
build(mid+,r,rc[num]);
dat[num]=;
}
void addx(int l,int r,int& num)
{
int t=num;num=mem++;
lc[num]=lc[t];rc[num]=rc[t];
if(l==r)
{
dat[num]=dat[t]+x;
return;
}
if(L<=mid) addx(l,mid,lc[num]);
else addx(mid+,r,rc[num]);
dat[num]=dat[lc[num]]+dat[rc[num]];
}
void add(int x){while(x<=n){addx(,totn,root[x]);x+=lowbit(x);}}
int tmpr[][],tmpx[];
int query(int L,int R,int k)//返回[L,R]内第k小的数
{
int tx,l=,r=totn,now,i;
for(tx=R,tmpr[][]=;tx>;tx-=lowbit(tx)) tmpr[][++tmpr[][]]=root[tx];
for(tx=L-,tmpr[][]=;tx>;tx-=lowbit(tx)) tmpr[][++tmpr[][]]=root[tx];
tmpx[]=root1[R];tmpx[]=root1[L-];
while(l!=r)
{
now=;
for(i=;i<=tmpr[][];i++) now+=dat[lc[tmpr[][i]]];
for(i=;i<=tmpr[][];i++) now-=dat[lc[tmpr[][i]]];
now+=dat[lc[tmpx[]]];now-=dat[lc[tmpx[]]];
if(now>=k)
{
r=mid;
for(i=;i<=tmpr[][];i++) tmpr[][i]=lc[tmpr[][i]];
for(i=;i<=tmpr[][];i++) tmpr[][i]=lc[tmpr[][i]];
tmpx[]=lc[tmpx[]];tmpx[]=lc[tmpx[]];
}
else
{
k-=now;l=mid+;
for(i=;i<=tmpr[][];i++) tmpr[][i]=rc[tmpr[][i]];
for(i=;i<=tmpr[][];i++) tmpr[][i]=rc[tmpr[][i]];
tmpx[]=rc[tmpx[]];tmpx[]=rc[tmpx[]];
}
}
return l;
}
int main()
{
int i,T;
scanf("%d",&T);
while(T--)
{
mem=t[]=;ma.clear();
scanf("%d%d",&n,&m);
for(i=;i<=n;i++)
{
scanf("%d",&a[i]);
t[++t[]]=a[i];
}
for(i=;i<=m;i++)
{
scanf("%s",tmp);
if(tmp[]=='Q')
{
scanf("%d%d%d",&q[i].i,&q[i].j,&q[i].x);
q[i].t=;
}
else if(tmp[]=='C')
{
scanf("%d%d",&q[i].i,&q[i].x);
q[i].t=;
t[++t[]]=q[i].x;
}
}
sort(t+,t+t[]+);
//for(i=1;i<=t[0];i++) printf("%d ",t[i]);puts("");
totn=unique(t+,t+t[]+)-t-;
for(i=;i<=totn;i++) ma[t[i]]=i,ma2[i]=t[i];
for(i=;i<=n;i++) a[i]=ma[a[i]];
for(i=;i<=m;i++)
{
if(q[i].t==)
{
q[i].x=ma[q[i].x];
//sz[q[i].x]++;
}
}
build(,totn,root[]);root1[]=root[];
for(i=;i<=n;i++) root1[i]=root1[i-],L=a[i],x=,addx(,totn,root1[i]);
for(i=;i<=n;i++) root[i]=root[];//dat[i]=dat[0];
//for(i=1;i<=n;i++) L=a[i],x=1,add(i);
for(i=;i<=m;i++)
{
if(q[i].t==)
printf("%d\n",ma2[query(q[i].i,q[i].j,q[i].x)]);
else if(q[i].t==)
{
L=query(q[i].i,q[i].i,);x=-;add(q[i].i);
L=q[i].x;x=;add(q[i].i);
}
}
}
return ;
}
upd:动态区间第k大(单点修改)不需要主席树,不需要可持久化,只需要普通的树状数组套线段树,内层动态开点即可
#include<cstdio>
#include<algorithm>
#define lowbit(x) ((x)&(-x))
#define mid ((l+r)>>1)
using namespace std;
const int totn=1e9;
struct Q
{
int t,i,j,x;
}q[];
int n,m,a[];
char tmp[];
int mem,root[];
int L,x;
int lc[],rc[],dat[];
void addx(int l,int r,int& num)
{
if(!num) num=++mem;
if(l==r)
{
dat[num]+=x;
return;
}
if(L<=mid) addx(l,mid,lc[num]);
else addx(mid+,r,rc[num]);
dat[num]=dat[lc[num]]+dat[rc[num]];
}
void add(int x){while(x<=n){addx(,totn,root[x]);x+=lowbit(x);}}
int tmpr[][];
int query(int L,int R,int k)//返回[L,R]内第k小的数
{
int tx,l=,r=totn,now,i;
for(tx=R,tmpr[][]=;tx>;tx-=lowbit(tx)) tmpr[][++tmpr[][]]=root[tx];
for(tx=L-,tmpr[][]=;tx>;tx-=lowbit(tx)) tmpr[][++tmpr[][]]=root[tx];
while(l!=r)
{
now=;
for(i=;i<=tmpr[][];i++) now+=dat[lc[tmpr[][i]]];
for(i=;i<=tmpr[][];i++) now-=dat[lc[tmpr[][i]]];
if(now>=k)
{
r=mid;
for(i=;i<=tmpr[][];i++) tmpr[][i]=lc[tmpr[][i]];
for(i=;i<=tmpr[][];i++) tmpr[][i]=lc[tmpr[][i]];
}
else
{
k-=now;l=mid+;
for(i=;i<=tmpr[][];i++) tmpr[][i]=rc[tmpr[][i]];
for(i=;i<=tmpr[][];i++) tmpr[][i]=rc[tmpr[][i]];
}
}
return l;
}
int main()
{
int i,l,r;
scanf("%d%d",&n,&m);
for(i=;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(i=;i<=m;i++)
{
scanf("%s",tmp);
if(tmp[]=='Q')
{
scanf("%d%d%d",&q[i].i,&q[i].j,&q[i].x);
//q[i].t=0;
}
else if(tmp[]=='C')
{
scanf("%d%d",&q[i].i,&q[i].x);
q[i].t=;
}
}
for(i=;i<=n;i++) L=a[i],x=,add(i);
for(i=;i<=m;i++)
{
//printf("%d\n",i);
if(q[i].t==)
{
printf("%d\n",query(q[i].i,q[i].j,q[i].x));
}
else if(q[i].t==)
{
L=query(q[i].i,q[i].i,);x=-;add(q[i].i);
L=q[i].x;x=;add(q[i].i);
}
}
return ;
}
upd:整体二分做此题(洛谷交的):
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct Q
{
int type,dat;
int pos,fl;
int l,r,num;
}q[],qt1[],qt2[];
int len,n,m,T;
int qnum,ans[];
int x[];
#define lowbit(x) ((x)&(-x))
struct BIT
{
int dat[];
void addx(int pos,int x){for(;pos<=n;pos+=lowbit(pos))dat[pos]+=x;}
int query(int pos){int ans=;for(;pos>;pos-=lowbit(pos))ans+=dat[pos];return ans;}
}tt;
//tt的第i位记录这一位是否小于等于mid
void work(int lp,int rp,int l,int r)
//在work之前,保证q[lp..rp]中询问都仅计算了所有小于l的修改操作的贡献,q[lp..rp]中不存在小于l的修改操作
{
if(lp>rp) return;//不可少
int i;
if(l==r)
{
for(i=lp;i<=rp;i++)
if(q[i].type==)
ans[q[i].num]=l;
return;
}
//tlen1=tlen2=0;
int tlen1=,tlen2=;
int mid=l+((r-l)>>),t;
for(i=lp;i<=rp;i++)
{
if(q[i].type==)
{
if(q[i].dat<=mid)
{
tt.addx(q[i].pos,q[i].fl);
qt1[++tlen1]=q[i];
}
else
qt2[++tlen2]=q[i];
}
else if(q[i].type==)
{
t=tt.query(q[i].r)-tt.query(q[i].l-);
/*if(q[i].dat>=t)
qt2[++tlen2]=q[i],qt2[tlen2].dat-=t;
else
qt1[++tlen1]=q[i];*/
if(q[i].dat<=t)
qt1[++tlen1]=q[i];
else
qt2[++tlen2]=q[i],qt2[tlen2].dat-=t;
}
}
for(i=lp;i<=rp;i++)
{
if(q[i].type==)
{
if(q[i].dat<=mid)
tt.addx(q[i].pos,-q[i].fl);
}
}
//不可能每一次都暴力重置整个树状数组,又必须重置,只能把操作反着做一遍
memcpy(q+lp,qt1+,sizeof(Q)*tlen1);
memcpy(q+lp+tlen1,qt2+,sizeof(Q)*tlen2);
work(lp,lp+tlen1-,l,mid);
work(lp+tlen1,rp,mid+,r);//如果不用临时变量作为tlen,执行到上一行后tlen就会改变,导致错误
}
int main()
{
int i,a,b,c;char tmp[];
scanf("%d%d",&n,&m);
len=qnum=;
for(i=;i<=n;i++)
{
scanf("%d",&x[i]);
q[++len].type=;q[len].pos=i;q[len].dat=x[i];q[len].fl=;
}
for(i=;i<=m;i++)
{
scanf("%s",tmp);
if(tmp[]=='Q')
{
scanf("%d%d%d",&a,&b,&c);
q[++len].type=;q[len].l=a;q[len].r=b;q[len].dat=c;q[len].num=++qnum;
}
else if(tmp[]=='C')
{
scanf("%d%d",&a,&b);
q[++len].type=;q[len].pos=a;q[len].dat=x[a];q[len].fl=-;
x[a]=b;
q[++len].type=;q[len].pos=a;q[len].dat=x[a];q[len].fl=;
}
}
work(,len,,);
for(i=;i<=qnum;i++) printf("%d\n",ans[i]);
return ;
}
以下仅做笔记
首先事实上带修改区间k小是不满足整体二分的要求的,要做一个小小的变换:将所有信息变为以下三种操作中的一种:给某个位置加上给定值的贡献,给某个位置删去给定值的贡献,查询某个区间的k小。(不知道为什么网上有叫做“插入”和”删除“的,然而整体二分并不能解决带插入区间第k小。。。。说不定有奇怪的方法可以?再说)
想象对于一个待处理的操作队列,以及现有的答案值域区间([l,r]),首先得到[l,r]的mid=l+(r-l)/2,然后对于所有查询操作,计算对其有贡献的(即发生在其之前的)所有值属于[l,mid]的修改操作(当然根据递归过程能保证不会有操作的值小于l的修改操作)对其的贡献(具体的话,由于这个顺序的要求,需要用一个树状数组来维护数组d[i](表示i位置的当前值带来的贡献,每一次”加上贡献“使其值加1,”删除贡献“使其值减1)的前缀和,这样在处理查询操作的时候就可以快速查询区间内有多少的贡献;由于不能暴力清零树状数组,只能把操作反着做一遍来清零)。
然后是分治过程:
顺次处理所有操作,并(不改变相对顺序地)将其分入两个队列。
对于某个查询操作,操作过程非常类似线段树上二分:如果当前答案(当前小于等于mid产生的贡献)小于需要的答案,就说明取mid作为答案还不够,那么将需要的答案减去当前答案,然后归入第二个队列;如果当前答案大于等于需要的答案,就说明取mid作为答案已经够/超出了,那么直接将询问归入第一个队列。(不需要考虑对第k小更加形式化的定义。。。原来想多了)
对于某个修改操作,如果其操作值小于等于mid,那么对第二个队列中查询操作的贡献都已经计算(在上面那个”减去当前答案“的过程中),因此只归入第一个队列;反之,说明其对第一个队列中查询操作不可能产生贡献,因此归入第二个队列。
然后,递归处理第一个队列以及其对应值域区间[l,mid],还有第二个队列及其对应值域区间[mid+1,r]。
当然,在函数中如果发现某一刻操作队列是空的,应该直接退出,不处理;如果发现l==r,说明当前操作队列中询问操作都已经找到正确答案,直接更新答案然后退出即可。
复杂度?任意一个操作只会log(值域)次出现在work函数中,设f(n)为一个函数(。。。。。),如果work函数中对于长度为n的操作序列的额外消耗时间(即除了递归处理以外的时间)是O(n*f(n))的,也就是对于任意一个操作的额外消耗时间是O(f(n))的(此题中f(n)=logn),因此总复杂度是O((n+q)f(n)log(值域))(n是序列长度,q是操作个数)
洛谷 P2617 Dynamic Rankings || ZOJ - 2112的更多相关文章
- 洛谷P2617 Dynamic Rankings (主席树)
洛谷P2617 Dynamic Rankings 题目描述 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a ...
- 2018.07.01洛谷P2617 Dynamic Rankings(带修主席树)
P2617 Dynamic Rankings 题目描述 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i ...
- 洛谷 P2617 Dynamic Rankings 解题报告
P2617 Dynamic Rankings 题目描述 给定一个含有\(n\)个数的序列\(a[1],a[2],a[3],\dots,a[n]\),程序必须回答这样的询问:对于给定的\(i,j,k\) ...
- 洛谷P2617 Dynamic Rankings
带修主席树模板题 主席树的单点修改就是把前缀和(大概)的形式改成用树状数组维护,每个树状数组的元素都套了一个主席树(相当于每个数组的元素root[i]都是主席树,且这个主席树维护了(i - lowbi ...
- 洛谷P2617 Dynamic Rankings 主席树 单点修改 区间查询第 K 大
我们将线段树套在树状数组上,查询前预处理出所有要一起移动的节点编号,并在查询过程中一起将这些节点移到左右子树上. Code: #include<cstdio> #include<cs ...
- 洛谷$P2617\ Dynamic\ Rankings$ 整体二分
正解:整体二分 解题报告: 传送门$w$ 阿查询带修区间第$k$小不显然整体二分板子呗,,, 就考虑先按时间戳排序(,,,其实并不需要读入的时候就按着时间戳排的鸭$QwQ$ 每次二分出$mid$先把所 ...
- 动态主席树【带修改】&& 例题 Dynamic Rankings ZOJ - 2112
参考链接:https://blog.csdn.net/WilliamSun0122/article/details/77885781 一.动态主席树介绍 动态主席树与静态主席树的不同在于:静态主席树不 ...
- 洛谷P2617 Dynamic Ranking(主席树,树套树,树状数组)
洛谷题目传送门 YCB巨佬对此题有详细的讲解.%YCB%请点这里 思路分析 不能套用静态主席树的方法了.因为的\(N\)个线段树相互纠缠,一旦改了一个点,整个主席树统统都要改一遍...... 话说我真 ...
- Dynamic Rankings ZOJ - 2112(主席树+树状数组)
The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with t ...
随机推荐
- Linux监測某一时刻对外的IP连接情况
相信大家都熟悉netstat命令吧,这里就主要採用此命令.网上流传的DDoS Deflate工具就是採用IP数量来统计对外连接数,然后结合Iptables的方法来实现某个IP增加黑名单和解禁某IP n ...
- Linux中查看文件或者文件夹大小
df -l 查看磁盘空间大小命令 df -hl 查看磁盘剩余空间 df -h 查看每个根路径的分区大小 du -sh 当前文件夹下所有文件大小(包括子文件大小 du -sm [文件夹] 返回该 ...
- [RxJS] Chain RxJS Operators Together with a Custom `pipe` Function using Array.reduce
Instead of writing complex operators, it's usually best to write simple, single-purpose operators th ...
- coco2dx新建项目报错,ld: -pie can only be used when targeting iOS 4.2 or later clang: error: linker command
在新建cocos2d-x以后,执行发现下面错误: ld: -pie can only be used when targeting iOS 4.2 or later clang: error: lin ...
- AnkhSVN介绍
AnkhSVN介绍 Posted on 2012-11-15 23:24 ArRan 阅读(3120) 评论(1) 编辑 收藏 AnkhSVN是一款在VS中管理Subversion的插件,您可以在VS ...
- C#实现如何判断一个数组中是否有重复的元素 返回一个数组升序排列后的位置信息--C#程序举例 求生欲很强的数据库 别跟我谈EF抵抗并发,敢问你到底会不会用EntityFramework
C#实现如何判断一个数组中是否有重复的元素 如何判断一个数组中是否有重复的元素 实现判断数组中是否包含有重复的元素方法 这里用C#代码给出实例 方法一:可以新建一个hashtable利用hasht ...
- Android经常使用设计模式(二)
继上一篇 Android经常使用设计模式(一)里认识了观察者.适配器.代理等三种模式,这一篇将会解说下面三种模式: 工厂模式 单例模式 命令模式 1.工厂模式(Factory Pattern) 工厂模 ...
- 【iOS系列】-单例模式的实现
1:重写allocWithZone方法 allocWithZone方法是对象分配内存空间时, alloc方法最终会调用这个方法 + (id)allocWithZone:(struct _NSZone ...
- Datagrid接收JSON数据格式
开打View下面的Shared创建一个视图模版(母版页)<!DOCTYPE html> <html> <head> <title>Main</ti ...
- PL/SQL Developer导入导出Oracle数据库方法
前一篇博客介绍了Navicat工具备份Oracle的方法.这篇博客介绍一下使用PL/SQL Developer工具导入导出Oracle数据库的方法. PL/SQL Developer是Oracle数据 ...