12.13日记
奇技淫巧
判断是不是2的幂:x > 0 ? ( x & (x - 1)) == 0 : false
CDQ
- P3374:树状数组单点加减+区间查询
思考CDQ的时候可以按照如下思路:
假设左右区间各自内部对内部的影响已经统计完了,并且都已经按照第二关键字(位置)排好序了。
那么首先,由于已经按照第一关键字(时间)排好序才开始CDQ的,所以左边的所有t都小于右边的t,第一维是可以保证的。
再之后,因为左右都按第二关键字排好序了,所以双指针,每次取较小的那一个,则之后的元素一定比刚取走的那个大(或者等于)。
接下来,考虑实际意义,左边区间的第一维小,所以是先进行的操作。那么显然,右区间修改操作对左区间无意义(因为还没改右边的,左边的就已经询问完了),左区间的修改操作对右区间的影响要看第二关键字,如果左区间的修改操作的位置比右边的询问小,那么是产生影响的,具体就是把询问拆成两部分,[1,l-1]和[1,r],询问到第一个就减,询问到第二个就加。注意离线操作是按照询问次序进行储存答案的。
另外,如果pos(第二关键字)相同该怎么办?必须再对第三关键字排序!原理很简单,根据实际意义,肯定是先统计修改,再处理询问操作,不然就会漏。
我就是挂在这两个地方。
#include
using namespace std;
const int M=5e5+20;
#define mid ((l+r)>>1)
#define LL long long
struct C{
int type,pos,val;
C(int a=0,int b=0,int c=0):type(a),pos(b),val(c){}//若type=1,val=修改数值,若type=2/3,val=询问位置
bool operator<(C x){
return pos
- P3810:三维偏序
还需要去重才行。
#include//还需要去重
using namespace std;
const int M=5e5+20;
#define mid ((l+r)>>1)
struct C{
int a,b,c,no;
C(int d=0,int e=0,int f=0,int g=0):a(d),b(e),c(f),no(g){}
bool operator<(const C &x)const{
if (a!=x.a)
return a
二逼平衡树
CDQ做法:
1 整体二分
就是二分答案跑cdq,
对于查询k大,我们得到左边的个数num,如果k<=num,去左边,否则k-=num,去右边
否则k<=mid分左,否则分右边。
对于查询rank,我们只在k>mid的时候,用num更新答案。
对于查询前驱,我们只在k>mid的时候,用左边的对应区间max更新答案(线段树维护即可)。
(我通过将所有数取反来将后继转化为前驱处理。)
此法较好实现。
设U为数的范围,时间(n+m)logUlogn。离散后应该是(n+m)lognlogn,但我懒得离散。。
目前拿了这题洛谷的rank2。
核心部分:
void solve(int L,int R,int l,int r)
{
if (l>r) return;
if (L==R)
{
for (i=l;i<=r;++i)
{
x=q[i];
if (a[x].type==2) ans[a[x].id]=L;
}
return;
}
int mid=(L+R)>>1;
t1=t2=t3=0;
for (i=l;i<=r;++i)
{
now=q[i];
if (a[now].type==2)
{
num=T.qiu_s(a[now].l,a[now].r);
if (nummid) q2[++t2]=now;
else q1[++t1]=now;
if (a[now].type==3)
{
if (a[now].x<=mid)
{
q3[++t3]=now;
T.add(a[now].l,a[now].id,a[now].x);
}
} else
if (a[now].x>mid)
if (a[now].type==4)
{
chmax(ans[a[now].id],T.qiu_m(a[now].l,a[now].r));
} else
ans[a[now].id]+=T.qiu_s(a[now].l,a[now].r);
}
}
while (t3) { now=q3[t3--];T.clear(a[now].l); }
int k=l-1;
for (i=1;i<=t1;++i) q[++k]=q1[i];
for (i=1;i<=t2;++i) q[k+i]=q2[i];
solve(L,mid,l,k);solve(mid+1,R,k+1,r);
}