Cdq分治整体二分学习记录
这点东西前前后后拖了好几个星期才学会……还是自己太菜啊。
Cdq分治的思想是:把问题序列分割成左右两个,先单独处理左边,再处理左边对右边的影响,再单独处理右边。这样可以消去数据结构上的一个log,降低编码复杂度。
整体二分:当一个询问的答案满足二分性质时,我们可以按照答案的大小分割整个查询和修改序列。每次把序列分成互不相同的两部分。这样能把数据结构的二分拿出来,降低编码复杂度。
说白了,就是当你懒得写树套树或者惨遭卡内存时候的骗分办法。
好了,上例题吧:
BZOJ2683:
二维单点加,矩形查。可以kdtree或树套树水过。
然而这里要用cdq分治。我们把一个查询利用二维前缀和原理拆成4个,然后按x排序,对t(时间)cdq分治,用树状数组维护y上的前缀和。我们先处理t在左边对右边查询的影响,然后再递归处理左右。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define debug cout
using namespace std;
const int maxn=8e5+1e2; int n; struct BinaryIndexTree {
int dat[maxn];
#define lowbit(x) (x&-x)
inline void update(int pos,int x) {
while( pos < maxn ) {
dat[pos] += x ,
pos += lowbit(pos);
}
}
inline int query(int pos) {
int ret = ;
while( pos )
ret += dat[pos] ,
pos -= lowbit(pos);
return ret;
}
}bit; struct QNode {
int tpe,x,y,aid,tme,lam;
friend bool operator < (const QNode &a,const QNode &b) {
if( a.x != b.x )
return a.x < b.x;
if( a.y != b.y )
return a.y < b.y;
return a.tpe < b.tpe;
}
}ns[maxn],tp[maxn]; int cnt,acn;
int ans[maxn]; inline void cdq(int l,int r) {
if( l == r )
return;
const int mid = ( l + r ) >> ;
for(int i=l;i<=r;i++) {
if( ns[i].tme <= mid && ns[i].tpe == )
bit.update(ns[i].y,ns[i].lam);
if( ns[i].tme > mid && ns[i].tpe == )
ans[ns[i].aid] += ns[i].lam * bit.query(ns[i].y);
}
for(int i=l;i<=r;i++)
if( ns[i].tme <= mid && ns[i].tpe == )
bit.update(ns[i].y,-ns[i].lam);
int l1 = l , l2 = mid + ;
for(int i=l;i<=r;i++)
if( ns[i].tme <= mid )
tp[l1++] = ns[i];
else
tp[l2++] = ns[i];
for(int i=l;i<=r;i++)
ns[i] = tp[i];
cdq(l,mid);
cdq(mid+,r);
} int main() {
static int t,x,y,xx,yy,lam;
scanf("%d",&n);
while( scanf("%d",&t) == && t != ) {
if( t == ) {
scanf("%d%d%d",&x,&y,&lam);
ns[++cnt].tpe = t , ns[cnt].x = x , ns[cnt].y = y ,
ns[cnt].lam = lam , ns[cnt].tme = cnt;
}
else {
scanf("%d%d%d%d",&x,&y,&xx,&yy);
--x , --y , ++acn;
ns[++cnt].tpe = t , ns[cnt].x = x , ns[cnt].y = y ,
ns[cnt].aid = acn , ns[cnt].tme = cnt , ns[cnt].lam = ;
ns[++cnt].tpe = t , ns[cnt].x = xx , ns[cnt].y = yy ,
ns[cnt].aid = acn , ns[cnt].tme = cnt , ns[cnt].lam = ;
ns[++cnt].tpe = t , ns[cnt].x = x , ns[cnt].y = yy ,
ns[cnt].aid = acn , ns[cnt].tme = cnt , ns[cnt].lam = -;
ns[++cnt].tpe = t , ns[cnt].x = xx , ns[cnt].y = y ,
ns[cnt].aid = acn , ns[cnt].tme = cnt , ns[cnt].lam = -;
}
}
sort(ns+,ns++cnt);
cdq(,cnt); for(int i=;i<=acn;i++)
printf("%d\n",ans[i]); return ;
}
BZOJ3110:
本来是一道树套树卡内存神题……
这题我们可以整体二分,对整个序列维护一个支持区间加,区间求和的树状数组(什么你不会?线段树也行),然后对于每个询问,如果>mid的区间和比查询的k小,则把询问分到左边,同时让k减去>mid的权值数量的区间和。反之直接分配到右边。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lli long long int
#define debug cout
using namespace std;
const int maxn=5e4+1e2; struct BinaryIndexTree {
lli org[maxn],del[maxn];
int siz;
#define lowbit(x) (x&-x) inline void cupd(lli* dst,int pos,int add) {
while( pos <= siz )
dst[pos] += add , pos += lowbit(pos);
}
inline lli cqry(lli* sou,int pos) {
lli ret = ;
while( pos )
ret += sou[pos] , pos -= lowbit(pos);
return ret;
}
inline lli qryp(int pos) {
return cqry(org,pos) + cqry(del,pos) * pos;
}
inline void update(int l,int r,int add) {
cupd(org,l,-(l-)*add);
cupd(org,r,r*add);
cupd(del,l,add);
cupd(del,r,-add);
}
inline lli query(int l,int r) {
return qryp(r) - qryp(l-);
}
inline void resize(int ss) {
siz = ss;
}
}bit; int ans[maxn]; struct QNode { // type == 1 means update , type == 2 means query;
int tpe,l,r,x,qid;
lli k;
}ns[maxn],tpl[maxn],tpr[maxn]; inline void solve(int ql,int qr,int al,int ar) {
if( al == ar ) {
for(int i=ql;i<=qr;i++)
if( ns[i].tpe == ) {
ans[ns[i].qid] = al;
}
return;
}
const int amid = ( al + ar ) >> ;
int cntl = , cntr = ;
for(int i=ql;i<=qr;i++) {
if( ns[i].tpe == ) {
if( ns[i].x <= amid ) {
tpl[++cntl] = ns[i];
} else {
bit.update(ns[i].l,ns[i].r,);
tpr[++cntr] = ns[i];
}
}
else {
lli lam = bit.query(ns[i].l,ns[i].r);
if( ns[i].k <= lam ) {
tpr[++cntr] = ns[i];
} else {
ns[i].k -= lam;
tpl[++cntl] = ns[i];
}
}
}
for(int i=;i<=cntr;i++)
if( tpr[i].tpe == )
bit.update(tpr[i].l,tpr[i].r,-); const int qmid = ql + cntl - ;
int c1 = ql , c2 = qmid + ;
for(int i=;i<=cntl;i++)
ns[c1++] = tpl[i];
for(int i=;i<=cntr;i++)
ns[c2++] = tpr[i]; solve(ql,qmid,al,amid);
solve(qmid+,qr,amid+,ar);
} inline int getint() {
int ret = , ch ;
while( !isdigit(ch=getchar()) );
do ret=ret*+ch-''; while( isdigit(ch=getchar()) );
return ret;
}
signed main() {
static int n,m,qid;
n = getint() , m = getint();
bit.resize(n);
for(int i=;i<=m;i++) {
ns[i].tpe = getint() , ns[i].l = getint() , ns[i].r = getint();
if( ns[i].tpe == ) {
scanf("%d",&ns[i].x);
} else {
scanf("%lld",&ns[i].k);
ns[i].qid = ++qid;
}
} solve(,m,-n,n); for(int i=;i<=qid;i++)
printf("%d\n",ans[i]); return ;
}
BZOJ1901:
单点修改,查第k小的值。我们只需要把修改变成一个删除原权值的操作和一个插入新权值的操作,然后维护一个单点修改区间求和的树状数组就可以了。注意zoj上这题的双精题卡内存。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define debug cout
using namespace std;
const int maxn=3e4+1e2; struct BinaryIndexTree {
int dat[maxn],siz; #define lowbit(x) (x&-x) inline void update(int pos,int x) {
while( pos <= siz )
dat[pos] += x , pos += lowbit(pos);
}
inline int query(int pos) {
int ret = ;
while( pos )
ret += dat[pos] , pos -= lowbit(pos);
return ret;
}
inline void resize(int ss) {
siz = ss;
}
}bit; struct QNode {
int tpe; // tpe == 1 means query , 0 means change
int pos,val,del;
int st,ed,k,qid;
}ns[maxn],tp[][maxn]; int in[maxn],srt[maxn],ans[maxn],len; inline void solve(int ql,int qr,int al,int ar) {
if( al == ar ) {
for(int i=ql;i<=qr;i++)
if( ns[i].tpe )
ans[ns[i].qid] = al;
return;
}
const int amid = ( al + ar ) >> ;
int cntl = , cntr = ;
for(int i=ql;i<=qr;i++) {
if( ns[i].tpe ) {
const int kk = bit.query(ns[i].ed) - bit.query(ns[i].st-);
if( ns[i].k > kk ) {
ns[i].k -= kk ,
tp[][++cntr] = ns[i];
} else tp[][++cntl] = ns[i];
} else {
if( ns[i].val <= amid ) {
bit.update(ns[i].pos,ns[i].del);
tp[][++cntl] = ns[i];
} else tp[][++cntr] = ns[i];
}
}
for(int i=ql;i<=qr;i++)
if( !ns[i].tpe && ns[i].val <= amid )
bit.update(ns[i].pos,-ns[i].del);
const int qmid = ql + cntl - ;
memcpy(ns+ql,tp[]+,sizeof(tp[][])*cntl);
memcpy(ns+qmid+,tp[]+,sizeof(tp[][])*cntr);
solve(ql,qmid,al,amid);
solve(qmid+,qr,amid+,ar);
} inline int getint() {
int ret = , ch;
while( !isdigit(ch=getchar()) );
do ret=ret*+ch-''; while( isdigit(ch=getchar()) );
return ret;
} int main() {
static int n,m,cnt,qcnt;
static char com[];
n = getint() , m = getint();
for(int i=;i<=n;i++) {
ns[++cnt].val = in[i] = srt[++len] = getint() ,
ns[cnt].pos = i;ns[cnt].del = ;
}
for(int i=,p;i<=m;i++) {
scanf("%s",com);
if( *com == 'Q' ) {
ns[++cnt].tpe = , ns[cnt].qid = ++qcnt;
ns[cnt].st = getint() , ns[cnt].ed = getint() , ns[cnt].k = getint();
} else {
p = getint();
ns[++cnt].pos = p , ns[cnt].val = in[p] , ns[cnt].del = -;
ns[++cnt].pos = p , srt[++len] = ns[cnt].val = in[p] = getint() , ns[cnt].del = ;
}
}
sort(srt+,srt++len) , len = unique(srt+,srt++len) - srt - ;
for(int i=;i<=cnt;i++)
if( !ns[i].tpe ) {
ns[i].val = lower_bound(srt+,srt++len,ns[i].val) - srt;
} bit.resize(n);
solve(,cnt,,len); for(int i=;i<=qcnt;i++)
printf("%d\n",srt[ans[i]]); return ;
}
BZOJ1146:
这题可以权值线段树套树链剖分的线段树,也可以带修主席树。总之就是卡内存+不是人写的……
所以我们要整体二分,同上一题,只不过变到了树上。所以我们需要树链剖分线段树。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define debug cerr
using namespace std;
const int maxn=8e4+1e2; struct QNode {
int tpe; // 1 means query , 0 means change
int k,a,b,qid;
int pos,val,add;
}ns[maxn<<],tp[][maxn<<]; int in[maxn],srt[maxn<<],ans[maxn],len;
int s[maxn],t[maxn<<],nxt[maxn<<];
int fa[maxn],dep[maxn],siz[maxn],son[maxn],top[maxn],num[maxn],cov[maxn];
int l[maxn<<],r[maxn<<],lson[maxn<<],rson[maxn<<],su[maxn<<],cnt;
int n,m; inline void build(int pos,int ll,int rr) {
l[pos] = ll , r[pos] = rr;
if( ll == rr ) return;
const int mid = ( ll + rr ) >> ;
build(lson[pos]=++cnt,ll,mid);
build(rson[pos]=++cnt,mid+,rr);
}
inline void update(int pos,int tar,int x) {
if( tar < l[pos] || r[pos] < tar ) return;
if( l[pos] == r[pos] && l[pos] == tar ) {
su[pos] += x;
return;
}
update(lson[pos],tar,x);
update(rson[pos],tar,x);
su[pos] = su[lson[pos]] + su[rson[pos]];
}
inline int query(int pos,int ll,int rr) {
if( rr < l[pos] || r[pos] < ll ) return ;
if( ll <= l[pos] && r[pos] <= rr ) return su[pos];
return query(lson[pos],ll,rr) + query(rson[pos],ll,rr);
} inline void addedge(int from,int to) {
static int cnt = ;
t[++cnt] = to , nxt[cnt] = s[from] , s[from] = cnt;
}
inline void pre(int pos) {
siz[pos] = ;
for(int at=s[pos];at;at=nxt[at])
if( t[at] != fa[pos] ) {
fa[t[at]] = pos , dep[t[at]] = dep[pos] + ;
pre(t[at]);
siz[pos] += siz[t[at]];
son[pos] = siz[t[at]] > siz[son[pos]] ? t[at] : son[pos];
}
}
inline void dfs(int pos) {
top[pos] = pos == son[fa[pos]] ? top[fa[pos]] : pos;
num[pos] = pos == son[fa[pos]] ? num[fa[pos]] + : ;
for(int at=s[pos];at;at=nxt[at])
if( t[at] != fa[pos] )
dfs(t[at]);
if( !son[pos] )
build(cov[top[pos]]=++cnt,num[top[pos]],num[pos]);
}
inline int chain(int a,int b) {
int ret = ;
while( top[a] != top[b] ) {
if( dep[top[a]] > dep[top[b]] ) {
ret += query(cov[top[a]],num[top[a]],num[a]),
a = fa[top[a]];
} else {
ret += query(cov[top[b]],num[top[b]],num[b]),
b = fa[top[b]];
}
}
ret += query(cov[top[a]],min(num[a],num[b]),max(num[a],num[b]));
return ret;
} inline void solve(int ql,int qr,int al,int ar) {
if( al == ar ) {
for(int i=ql;i<=qr;i++)
if( ns[i].tpe )
ans[ns[i].qid] = al;
return;
}
const int amid = ( al + ar ) >> ;
int cntl = , cntr = ;
for(int i=ql;i<=qr;i++) {
if( ns[i].tpe ) {
int kk = chain(ns[i].a,ns[i].b);
if( ns[i].k > kk ) {
ns[i].k -= kk , tp[][++cntl] = ns[i];
} else tp[][++cntr] = ns[i];
} else {
if( ns[i].val > amid ) {
const int &pos = ns[i].pos;
update(cov[top[pos]],num[pos],ns[i].add);
tp[][++cntr] = ns[i];
} else tp[][++cntl] = ns[i];
}
}
for(int i=qr;i>=ql;i--)
if( ns[i].val > amid ) {
const int &pos = ns[i].pos;
update(cov[top[pos]],num[pos],-ns[i].add);
}
const int qmid = ql + cntl - ; memcpy( ns + ql , tp[] + , sizeof(tp[][]) * cntl );
memcpy( ns + qmid + , tp[] + , sizeof(tp[][]) * cntr ); solve(ql,qmid,al,amid);
solve(qmid+,qr,amid+,ar);
} inline int getint() {
int ret = , ch;
while( !isdigit(ch=getchar()) );
do ret=ret*+ch-''; while( isdigit(ch=getchar()) );
return ret;
}
int main() {
static int qcnt,qlen;
n = getint() , m = getint();
qlen = n;
for(int i=;i<=n;i++)
ns[i].pos = i , in[i] = ns[i].val = srt[++len] = getint() , ns[i].add = ;
for(int i=,a,b;i<n;i++) {
a = getint() , b = getint();
addedge(a,b) , addedge(b,a);
}
for(int i=,k,p;i<=m;i++) {
k = getint();
if( !k ) {
p = getint();
++qlen , ns[qlen].pos = p , ns[qlen].val = in[p] , ns[qlen].add = -;
++qlen , ns[qlen].pos = p , in[p] = ns[qlen].val = srt[++len] = getint() , ns[qlen].add = ;
} else {
++qlen , ns[qlen].tpe = , ns[qlen].a = getint() , ns[qlen].b = getint() , ns[qlen].k = k , ns[qlen].qid = ++qcnt;
}
} sort(srt+,srt++len);
len = unique(srt+,srt++len) - srt - ;
for(int i=;i<=qlen;i++)
if( ! ns[i].tpe ) ns[i].val = lower_bound(srt+,srt++len,ns[i].val) - srt; pre();dfs();
solve(,qlen,,len); for(int i=;i<=qcnt;i++)
if( ans[i] ) printf("%d\n",srt[ans[i]]);
else puts("invalid request!"); return ;
}
BZOJ3237:
删除一组边问你是否联通。我们可以cdq分治求解。
维护每条边被删除的次数,递归左右时,如果这条边在某一侧被删除次数为0了则合并两边点所在的并查集。这里不要路径压缩,我们需要按秩合并。我们可以记录一个栈来回滚操作(当然你非得用主席树可持久化那也行)。查询时就看一下点1所在的联通块大小是否为n即可。是不是很好写啊?
代码:
#include<cstdio>
using namespace std;
const int maxn=1e5+1e2,maxm=2e5+1e2,maxk=1e5+1e2; int fa[maxn],siz[maxn];
int a[maxm],b[maxm];
int len[maxk],met[maxk][];
int cnt[maxm],stk[maxn],top;
char ans[maxk];
int n; inline int findfa(int x) {
return fa[x] == x ? x : findfa(fa[x]);
} inline void merge(int l,int r) {
for(int i=l;i<=r;i++)
for(int j=;j<=len[i];j++) {
const int k = met[i][j];
if( --cnt[k] ) continue;
int aa = findfa(a[k]) , bb = findfa(b[k]);
if( aa == bb ) continue;
if( siz[aa] > siz[bb] ) fa[bb] = aa , siz[aa] += siz[bb] , stk[++top] = bb;
else fa[aa] = bb , siz[bb] += siz[aa] , stk[++top] = aa;
}
}
inline void resetcnt(int l,int r) {
for(int i=l;i<=r;i++)
for(int j=;j<=len[i];j++)
++cnt[met[i][j]];
}
inline void resetfa(int tar) {
while( top > tar ) {
const int pos = stk[top--];
siz[fa[pos]] -= siz[pos] , fa[pos] = pos;
}
} inline void solve(int ll,int rr) {
if( ll == rr ) {
ans[ll] = ( siz[findfa()] == n );
return;
}
const int mid = ( ll + rr ) >> , mem = top; merge(ll,mid);
solve(mid+,rr);
resetfa(mem);
resetcnt(ll,mid); merge(mid+,rr);
solve(ll,mid);
resetfa(mem);
resetcnt(mid+,rr);
} inline void init() {
for(int i=;i<=n;i++)
fa[i] = i , siz[i] = ;
} int main() {
static int m,k;
scanf("%d%d",&n,&m);
for(int i=;i<=m;i++)
scanf("%d%d",a+i,b+i);
scanf("%d",&k);
for(int i=;i<=k;i++) {
scanf("%d",len+i);
for(int j=;j<=len[i];j++)
scanf("%d",met[i]+j);
} init();
resetcnt(,k);
for(int i=;i<=m;i++)
if( !cnt[i] ) {
int aa = findfa(a[i]) , bb = findfa(b[i]);
if( aa == bb ) continue;
if( siz[aa] > siz[bb] ) fa[bb] = aa , siz[aa] += siz[bb];
else fa[aa] = bb , siz[bb] += siz[aa];
} solve(,k); for(int i=;i<=k;i++)
puts( ans[i] ? "Connected" : "Disconnected" ); return ;
}
BZOJ2001:
动态维护最小生成树。
我们可以LCT,可做但据说并不能AC(常数巨大)。
所以我们需要cdq分治。对于每层,我们删除无用边,缩点必需边,可以把边数化成O(n)级别的,然后再向下递归即可。底层直接更新权值,计算最小生成树,记录答案。
实现的时候,我们记录每一条边实际权值,和它原来的编号和在当前边序列中的编号的映射关系即可。每层并查集初始化的时候仅初始化在这层存在的边。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define lli long long int
using namespace std;
const int maxn=2e4+1e2,maxm=5e4+1e2,maxd=;
const int inf=0x3f3f3f3f; struct Edge {
int a,b,val,id;
friend bool operator < (const Edge &a,const Edge &b) {
return a.val < b.val;
}
}ess[maxd][maxm],now[maxm],etp[maxm];
int opeid[maxm],opeval[maxm];
int wei[maxm],cov[maxm]; // weight of edge , convert real id to operated id.
int fa[maxn];
lli ans[maxm]; inline int findfa(int x) {
return fa[x] == x ? x : fa[x] = findfa(fa[x]);
}
inline void merge(int x,int y) {
x = findfa(x) , y = findfa(y);
if( x == y ) return;
fa[x] = y;
}
inline void reset(Edge* es,int len) {
for(int i=;i<=len;i++)
fa[es[i].a] = es[i].a ,
fa[es[i].b] = es[i].b;
} inline lli contraction(Edge* es,int &len) {
lli ret = ;
int tpl = ; reset(es,len);
sort(es+,es++len);
for(int i=;i<=len;i++)
if( findfa(es[i].a) != findfa(es[i].b) ) { // find a mst
etp[++tpl] = es[i] , merge(es[i].a,es[i].b);
} reset(etp,tpl);
for(int i=;i<=tpl;i++)
if( etp[i].val != -inf ) {
ret += etp[i].val , merge(etp[i].a,etp[i].b);
} tpl = ;
for(int i=;i<=len;i++)
if( findfa(es[i].a) != findfa(es[i].b) ) {
etp[++tpl] = es[i];
etp[tpl].a = findfa(es[i].a) , etp[tpl].b = findfa(es[i].b) ,
cov[es[i].id] = tpl;
} reset(es,len);
len = tpl;
memcpy(es+,etp+,sizeof(es[])*len); return ret;
}
inline void reduction(Edge* es,int &len) {
int tpl = ; reset(es,len);
sort(es+,es++len);
for(int i=;i<=len;i++)
if( findfa(es[i].a) != findfa(es[i].b) ) {
merge(es[i].a,es[i].b);
etp[++tpl] = es[i];
cov[es[i].id] = tpl;
}
else if( es[i].val == inf ) {
etp[++tpl] = es[i];
cov[es[i].id] = tpl;
} reset(es,len) , len = tpl;
memcpy(es+,etp+,sizeof(es[])*len);
}
inline lli mst(Edge* es,int len) {
lli ret = ;
reset(es,len);
sort(es+,es++len);
for(int i=;i<=len;i++)
if( findfa(es[i].a) != findfa(es[i].b) )
ret += es[i].val , merge(es[i].a,es[i].b);
return ret;
} inline void solve(Edge* es,int dep,int len,int ll,int rr,lli res) {
if( ll == rr )
wei[opeid[ll]] = opeval[ll];
for(int i=;i<=len;i++) {
es[i].val = wei[es[i].id];
now[i] = es[i] , cov[now[i].id] = i;
}
if( ll == rr ) {
ans[ll] = res + mst(now,len);
return;
} for(int i=ll;i<=rr;i++) {
now[cov[opeid[i]]].val = -inf;
}
res += contraction(now,len); for(int i=ll;i<=rr;i++)
now[cov[opeid[i]]].val = inf;
reduction(now,len); memcpy(ess[dep+]+,now+,sizeof(now[])*len); const int mid = ( ll + rr ) >> ;
solve(ess[dep+],dep+,len,ll,mid,res);
solve(ess[dep+],dep+,len,mid+,rr,res);
} inline char nextchar() {
static char buf[<<],*st=buf+(<<),*ed=buf+(<<);
if( st == ed ) ed = buf + fread(st=buf,,<<,stdin);
return st != ed ? *st++ : -;
}
inline int getint() {
int ret = ,ch;
while( !isdigit(ch=nextchar()) );
do ret=ret*+ch-''; while( isdigit(ch=nextchar()) );
return ret;
}
int main() {
static int n,m,q;
scanf("%d%d%d",&n,&m,&q);
for(int i=,a,b,l;i<=m;i++) {
scanf("%d%d%d",&a,&b,&l);
ess[][i] = (Edge){a,b,l,i};
wei[i] = l;
}
for(int i=;i<=q;i++)
scanf("%d%d",opeid+i,opeval+i); solve(ess[],,m,,q,); for(int i=;i<=q;i++)
printf("%lld\n",ans[i]); return ;
}
为什么代码都写得这么长这么丑还常数那么大?……果然还是自己太菜啊。
Cdq分治整体二分学习记录的更多相关文章
- CDQ分治&整体二分学习个人小结
目录 小结 CDQ分治 二维LIS 第一道裸题 bzoj1176 Mokia bzoj3262 陌上花开 bzoj 1790 矩形藏宝地 hdu5126四维偏序 P3157 [CQOI2011]动态逆 ...
- 一篇自己都看不懂的CDQ分治&整体二分学习笔记
作为一个永不咕咕咕的博主,我来更笔记辣qaq CDQ分治 CDQ分治的思想还是比较简单的.它的基本流程是: \(1.\)将所有修改操作和查询操作按照时间顺序并在一起,形成一段序列.显然,会影响查询操作 ...
- [学习笔记] CDQ分治&整体二分
突然诈尸.png 这两个东西好像都是离线骗分大法... 不过其实这两个东西并不是一样的... 虽然代码长得比较像 CDQ分治 基本思想 其实CDQ分治的基本思想挺简单的... 大概思路就是长这样的: ...
- 算法笔记--CDQ分治 && 整体二分
参考:https://www.luogu.org/blog/Owencodeisking/post-xue-xi-bi-ji-cdq-fen-zhi-hu-zheng-ti-er-fen 前置技能:树 ...
- CQD(陈丹琦)分治 & 整体二分——专题小结
整体二分和CDQ分治 有一些问题很多时间都坑在斜率和凸壳上了么--感觉斜率和凸壳各种搞不懂-- 整体二分 整体二分的资料好像不是很多,我在网上找到了一篇不错的资料: 整体二分是个很神的东西 ...
- CDQ分治与整体二分学习笔记
CDQ分治部分 CDQ分治是用分治的方法解决一系列类似偏序问题的分治方法,一般可以用KD-tree.树套树或权值线段树代替. 三维偏序,是一种类似LIS的东西,但是LIS的关键字只有两个,数组下标和 ...
- CDQ分治 & 整体分治
Part 1:CDQ分治 CDQ分治讲解博客 可以把CDQ分治理解为类似与归并排序求逆序对个数的一种分治算法(至少我现在是这么想的).先处理完左右两边各自对答案的贡献,在处理跨越左右两边的对答案的贡献 ...
- CodeForces - 762E:Radio stations (CDQ分治||排序二分)
In the lattice points of the coordinate line there are n radio stations, the i-th of which is descri ...
- luogu P5473 [NOI2019]I 君的探险 交互 随机 二分 分治 整体二分
LINK:I 君的探险 神仙题! 考虑一个暴力的做法 每次点亮一个点 询问全部点 这样询问次数为 \(\frac{n\cdot (n-1)}{2}\) 可以通过前5个点. 考虑都为A的部分分 发现一个 ...
随机推荐
- 使用Python的turtle(海龟)模块画图
第一步:让Python引入turtle模块,引入模块就是告诉Python你想要用它. import turtle 第二步:创建画布.调用turtle中的Pen函数 t = turtle.Pen() 第 ...
- POJ 2932 圆扫描线
求n个圆中没有被包含的圆.模仿扫描线从左往右扫,到左边界此时如有3个交点,则有3种情况,以此判定该圆是否被离它最近的圆包含,而交点和最近的圆可以用以y高度排序的Set来维护.因此每次到左边界插入该圆, ...
- Algorithm(1) - Karatsuba multiplication
这个系列主要是记一下目前效率较高或者比较出名的一些算法. Karatsuba multiplication: x=5678 then: a=56 b=67 y=1234 c= ...
- SpringSecurity csrf验证忽略某些请求
前几天项目中遇到springSecurity问题,研究了大半天,掉进了csrf的坑,先认识一下csrf CSRF概念:CSRF跨站点请求伪造(Cross—Site Request Forgery),跟 ...
- 第13月第25天 ios11 uitableview reloaddata contentsize
1. [tableView reloadData]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_ ...
- Thinkpad X220 升级 Windows 10 后无线网卡消失问题
11年购买的Thinkpad X220从Win7升级到Win10后,用着还是挺顺手的,网络显示等一切正常,直到今天合上盖子电脑睡眠以后再次打开,wifi消失不见.重启,关机再开机,都没用,只显示有线网 ...
- Java Service Wrapper将java程序设置为服务
有时候我们希望我们java写的程序作为服务注册到系统中,Java Service Wrapper(下面简称wrapper)是目前较为流行的将Java程序部署成Windows服务的解决方案, 本文将讨论 ...
- win10下安装MinGW-w64 - for 32 and 64 bit Windows
对于不经常使用c语言的同学来说,只需要安装MinGW-w64 - for 32 and 64 bit Windows,就可以使用GCC在命令行对c源码进行编译. 首先打开命令行检查自己是否已经安装了g ...
- linux中serial driver理解【转】
转自:http://blog.csdn.net/laoliu_lcl/article/details/39967225 英文文档地址:myandroid/kernel_imx/Documentatio ...
- make distclean
清空bin目录make dirclean 清空所有相关的东西,包括下载的软件包,配置文件,feeds内容等make distclean 这个命令会删除feeds目录及其下面的所有的文件,直接结果就是运 ...