写的让人看不懂,仅留作笔记

静态主席树,相当于前缀和套(可持久化方法构建的)值域线段树。

建树方法:记录前缀和的各位置的线段树的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的更多相关文章

  1. 洛谷P2617 Dynamic Rankings (主席树)

    洛谷P2617 Dynamic Rankings 题目描述 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a ...

  2. 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 ...

  3. 洛谷 P2617 Dynamic Rankings 解题报告

    P2617 Dynamic Rankings 题目描述 给定一个含有\(n\)个数的序列\(a[1],a[2],a[3],\dots,a[n]\),程序必须回答这样的询问:对于给定的\(i,j,k\) ...

  4. 洛谷P2617 Dynamic Rankings

    带修主席树模板题 主席树的单点修改就是把前缀和(大概)的形式改成用树状数组维护,每个树状数组的元素都套了一个主席树(相当于每个数组的元素root[i]都是主席树,且这个主席树维护了(i - lowbi ...

  5. 洛谷P2617 Dynamic Rankings 主席树 单点修改 区间查询第 K 大

    我们将线段树套在树状数组上,查询前预处理出所有要一起移动的节点编号,并在查询过程中一起将这些节点移到左右子树上. Code: #include<cstdio> #include<cs ...

  6. 洛谷$P2617\ Dynamic\ Rankings$ 整体二分

    正解:整体二分 解题报告: 传送门$w$ 阿查询带修区间第$k$小不显然整体二分板子呗,,, 就考虑先按时间戳排序(,,,其实并不需要读入的时候就按着时间戳排的鸭$QwQ$ 每次二分出$mid$先把所 ...

  7. 动态主席树【带修改】&& 例题 Dynamic Rankings ZOJ - 2112

    参考链接:https://blog.csdn.net/WilliamSun0122/article/details/77885781 一.动态主席树介绍 动态主席树与静态主席树的不同在于:静态主席树不 ...

  8. 洛谷P2617 Dynamic Ranking(主席树,树套树,树状数组)

    洛谷题目传送门 YCB巨佬对此题有详细的讲解.%YCB%请点这里 思路分析 不能套用静态主席树的方法了.因为的\(N\)个线段树相互纠缠,一旦改了一个点,整个主席树统统都要改一遍...... 话说我真 ...

  9. Dynamic Rankings ZOJ - 2112(主席树+树状数组)

    The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with t ...

随机推荐

  1. 新手玩个人server(阿里云)续二

    小二班一番厮杀:那英四强诞生:大家闺秀,小家碧玉.窈窕淑女,妍姿俊俏 .不解释! ?不行! 陈冰,李嘉格,刘明湘.张碧晨.大多数的时候,仅仅要脸好看,一切都那么自热而然的顺理成章. 尽管网上骂声四起, ...

  2. 如何利用Fluxion诱惑目标用户获取WPA密码

      前言 由于ISP替代了易受攻击的路由器,供渗透测试人员选择的诸如Reaver这样的工具越来越少,对于特定的目标,哪些工具有用与否能够确定的也很少.而如果采用暴力破解WPA密码,可能会需要大量的时间 ...

  3. ln 软连接 & 硬连接

    创建软连接的方式 #ln -s soure /file object 创建软连接是连接文件本身,可以跨分区建立软连接,不会应为不同分区而出现不能使用的问题. 在创建软连接的文件中,修改一处文件另一处同 ...

  4. 杭电1232畅通project

    畅通project Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total ...

  5. linux系列之-—03 常见问题

    问题1 描述:Linux如何查看JDK的安装路径 问题2 描述:执行shell脚本时报错,错误信息为:bash: line 19: jar: command not found 原因:因为在系统环境变 ...

  6. 基于空间直方图meanshift跟踪

    近期看了一篇文章<spatiograms versus histograms for region-based tracking>,在此把这篇文章的核心思想及算法推理进行整理. 空间直方图 ...

  7. C#&.NET高级面试题

    转自http://chaoyouzhuo.blog.163.com/blog/static/1263760012011109114131316/ 1. DateTime.Parse(myString) ...

  8. Servlet第七课:ServletContext HttpSession 以及HttpServletRequest之间的关系

    课程目标: ① 在Servlet中懂得ServletContext HttpSession 以及HttpServletRequest之间的关系 ② 懂得怎样使用它们 概念介绍: 1. [共同点]不管对 ...

  9. 为什么java构造函数的构造器只能在第一行写this() 或者super() ?

    最近在看内部类, 但是被“为什么匿名内部类或者局部内部类使用方法的局部变量时, 局部变量一定得是final类型”困扰着, 在网上查找资料的时候, 发现我对类初始化完全不了解, 之前的认识都是错误! 所 ...

  10. getifaddrs

    getifaddrs 获取本地网络接口的信息.在路由器上可以用这个接口来获取wan/lan等接口当前的ip地址,广播地址等信息. #include <sys/types.h> #inclu ...