CDQ分治&整体二分九连测

CDQ分治学习及例题总结 今天学了一下cdq分治,先大体说一下cdq分治的性质以及用法。 使用cdq分治的条件: 修改操作对询问的贡献独立,修改操作相互不影响 题目可以使用离线算法,不必强制在线(询问次数可以保存在数组) cdq分治的性质: cdq分治通过对时间复杂度增加一个log来降维 cdq可以用来代替复杂的数据结构 在cdq分治中,对于划分出来的两个区间,前一个子问题需要用来解决后一个子问题。 cdq使用步骤... 阅读详情


整体二分好难a
CDQ分治&整体二分九连:

A[适者]

先来一个不是cdq分治的题(虽然也可以用分治做)
首先先来考虑按照什么顺序来搞掉这些机甲
对于两个相邻的机甲i,j,先i后j的代价是 Ai∗Di+(Di+Dj)∗Aj A i ∗ D i + ( D i + D j ) ∗ A j
那么如果i应该比j先消灭就有
Ai∗Di+(Di+Dj)∗Aj<Aj∗Dj+(Di+Dj)∗Ai A i ∗ D i + ( D i + D j ) ∗ A j < A j ∗ D j + ( D i + D j ) ∗ A i
也就是 Aj∗Di<Dj∗Ai A j ∗ D i < D j ∗ A i
所以将原序列按照 Di/Ai D i / A i 做关键字排序
让后考虑怎么用这两个大招
我们设 Ci C i 表示干掉第i个人的收益
那么可以得出 Ci=Ai∗(∑ij=1Dj)+Di∗(∑nj=i+1Aj) C i = A i ∗ ( ∑ j = 1 i D j ) + D i ∗ ( ∑ j = i + 1 n A j )
那么,同时干掉两个人i,j(i<j)的收益就是 Ci+Cj−Ai∗Dj C i + C j − A i ∗ D j
我们用斜率dp来解决这个问题,设干掉的第二个人是i时的最大收益为 fi f i
得到转移式子 fi=max{Ci+Cj−Aj∗Di} f i = m a x { C i + C j − A j ∗ D i }
化简一下
Cj=Aj∗Di−fi+Ci C j = A j ∗ D i − f i + C i
发现凸包上面的点的x坐标 (Aj) ( A j ) 不是单调的,而且询问的斜率 Di D i 也不是单调的
所以我们需要采取一下一项:
1.splay维护凸包
2.cdq分治
我这次还是坚定地选择了前者毕竟跑的快(不要认为splay常数大就跑的慢,实际凸包上的点并不会很多所以通常是比cdq快很多的让后就被D了)

#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define db double
#define N 300010
#define eps 1e-6
#define LL long long
#define son(x) (s[f[x]][1]==x)
using namespace std;
struct p{ int d,t; } w[300010];
int n,A,q[N],s[N][2],f[N],rt,cnt; db l[N],r[N];
LL ans,C[N],S[N],T[N],D[N],SD[N];
inline bool c1(p a,p b){
    return a.t*b.d<b.t*a.d;
}
inline db slp(int j,int k){
    if(T[j]==T[k]) return C[k]>C[j]?1e8:-1e8;
    return (C[j]-C[k])/(db)(T[j]-T[k]);
}
inline void rot(int x){
    int p=f[x],g=f[p],d=son(x);
    s[p][d]=s[x][!d]; f[s[p][d]]=p;
    if(g) s[g][son(p)]=x; f[x]=g; 
    s[x][!d]=p; f[p]=x;
}
inline void splay(int x,int r=0){
    if(!x) return;
    for(int p;(p=f[x])!=r;rot(x))
        if(f[p]!=r && son(x)==son(p)) rot(p);
    if(!r) rt=x;
}
inline void insert(int k){
    if(!rt){ rt=k; return; }
    for(int x=rt;;){
        int d=T[x]<T[k];
        if(!s[x][d]){ s[x][d]=k; f[k]=x; splay(k); return; }
        x=s[x][d];
    }
}
inline int gPre(int k){
    int r=0;
    for(int x=s[k][0];x;){
        if(slp(x,k)<=l[x]+eps) r=x,x=s[x][1];
        else x=s[x][0];
    }
    return r;
}
inline int gSuc(int k){
    int l=0;
    for(int x=s[k][1];x;){
        if(slp(x,k)+eps>=r[x]) l=x,x=s[x][0];
        else x=s[x][1];
    }
    return l;
}
inline void ps(int k){
    int p;
    if(s[k][0]){
        p=gPre(k);
        splay(p,k);
        f[s[p][1]]=0;
        s[p][1]=0;
        l[k]=r[p]=slp(k,p);
    } else l[k]=1e9;
    if(s[k][1]){
        p=gSuc(k);
        splay(p,k);
        f[s[p][0]]=0;
        s[p][0]=0;
        r[k]=l[p]=slp(k,p);
    } else r[k]=-1e9;
}
inline void maintain(int k){
    splay(k); ps(k); int p;
    if(l[k]<=r[k]){
        if(!s[k][0] || !s[k][1]){
            rt=s[k][0]+s[k][1];
            f[rt]=0; ps(rt);
        } else {
            p=gSuc(k);
            splay(p,k);
            rt=s[k][0];
            f[rt]=0;
            s[rt][1]=p;
            f[p]=rt;
            ps(p); ps(rt);
        }
    }
}
inline int find(db k){
    if(!rt) return 0;
    for(int x=rt;x;){
        if(r[x]>k+eps) x=s[x][1];
        else if(l[x]+eps<k) x=s[x][0];
        else return x;
    }
}
int main(){
    scanf("%d%d",&n,&A);
    for(int i=1;i<=n;++i){
        scanf("%d%d",&w[i].d,&w[i].t);
        w[i].t=w[i].t/A+(w[i].t%A>0);
    }
    sort(w+1,w+1+n,c1); *S=-1;
    for(int i=1;i<=n;++i){
        T[i]=w[i].t;
        D[i]=w[i].d;
        S[i]=S[i-1]+T[i];
        C[i]=S[i]*D[i];
    }
    LL SS=0;
    for(int i=1;i<=n;++i) SS+=C[i];
    for(int i=n;i;--i){
        SD[i]=SD[i+1]+D[i];
        C[i]+=SD[i+1]*T[i];
    }
    insert(1); maintain(1);
    for(int i=2;i<=n;++i){
        int j=find(D[i]);
        ans=max(ans,C[i]+C[j]-D[i]*T[j]);
        insert(i); maintain(i);
    }
    printf("%lld\n",SS-ans);
}



B[陌上花开]

正文开始,顺便口胡cdq分治的原理
题意大概就是你有几个限制让后求满足的(i<j)的个数
那我们考虑一下怎么做
首先我们将所有的花按照 ai a i 排序,那么就只有前面i的能为后面的j给出贡献
让后,如果没有 ci c i 这个限制就可以用树状数组统计 bi<bj b i < b j 的个数了
现在加上c的限制,我们考虑分治
将整个序列分为[l,mid]和[mid+1,r]
让后分别按照b作为关键字排序
接下来,类似归并排序的操作,我们将这两个部分重新合并到一起,设i,j分别为两个部分未处理的第一个元素
若 bi<bj b i < b j 我们将 ci c i 加入BIT,否则我们询问BIT中有多少个小于 cj c j 的并加入答案
注意到我们这样打乱了原来按照a排序的顺序,所以我们先递归到两个子区间去处理答案,这样处理完之后答案就自然是按照b有序的了,也省掉了排序这个步骤了
最后注意全等的情况

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100010
using namespace std;
int n,k,w[N<<1],A[N],c[N];
struct fl{ int a,b,c,r; } a[N],b[N];
inline bool c1(fl a,fl b){
    return a.a==b.a?(a.b==b.b?(a.c==b.c?a.r<b.r:a.c<b.c):a.b<b.b):a.a<b.a;
}
inline void cdq(int l,int r){
    if(l==r) return;
    int m=l+r>>1,i=l,j=m+1,t=l;
    cdq(l,m); cdq(m+1,r);
    for(;i<=m && j<=r;){
        if(a[i].b<=a[j].b){
            for(int x=a[i].c;x<=k;x+=x&-x) ++w[x];
            b[t++]=a[i++];
        } else {
            for(int x=a[j].c;x;x&=x-1) A[a[j].r]+=w[x];
            b[t++]=a[j++];
        }
    }
    for(;i<=m;) b[t++]=a[i++];
    for(;j<=r;){
        for(int x=a[j].c;x;x&=x-1) A[a[j].r]+=w[x];
        b[t++]=a[j++];
    }
    for(;l<=r;++l){
        for(int x=b[l].c;x<=k;x+=x&-x) w[x]=0;
        a[l]=b[l];
    }
}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i)
        scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c),a[i].r=i;
    sort(a+1,a+1+n,c1);
    cdq(1,n);
    sort(a+1,a+1+n,c1);
    for(int i=n-1;i;--i) if(a[i].a==a[i+1].a &&a[i].b==a[i+1].b && a[i].c==a[i+1].c){
        A[a[i].r]=max(A[a[i].r],A[a[i+1].r]);
    }
    for(int i=1;i<=n;++i) ++c[A[i]];
    for(int i=0;i<n;++i) printf("%d\n",c[i]);
}



C[拦截导弹]

就一个LIS做完啦
不光是二维LIS还要计算方案还不取模真不知道出题人想干什么
还好数据比较水double就存下了
首先要知道在LIS中出现的条件是什么
我们设 fi f i 表示以i结尾的LIS的长度,那么转移很简单 fi=max(fj+1),vj<vi,hj<hi f i = m a x ( f j + 1 ) , v j < v i , h j < h i
让后我们将整个序列取反并翻转做一次LIS就得到了 f′i f i ′
如果有 fi+f′i=|LIS|+1 f i + f i ′ = | L I S | + 1 那么i就可以出现在LIS之中
另外,i被拦截的概率就是通过i的方案数/总方案数
我们设 gi,g′i g i , g i ′ 表示正着做和倒着做的方案数,那么概率就是 p=gi∗g′i∑fi=|LIS|gi p = g i ∗ g i ′ ∑ f i = | L I S | g i
这时候我们又面临一个选择
1.树套树维护f和g
2.cdq分治
那肯定选分治啦,树套树哪里比得上cdq分治2333

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 50010
#define db double
using namespace std;
int n,m,w[N],T; db W[N];
struct P{ int v,h,r,f[2]; db g[2]; } s[N],b[N];
inline bool cv(P a,P b){ return a.v>b.v; }
inline int gmx(int& x,int y){ return x<y?(x=y)|1:0; }
inline void add(P s){
    int f=s.f[T]; db g=s.g[T];
    for(int x=s.h;x<=m;x+=x&-x)
        if(gmx(w[x],f)) W[x]=g; 
        else if(w[x]==f) W[x]+=g;
}
inline void query(P& s){
    int& f=s.f[T]; db& g=s.g[T];
    for(int x=s.h;x;x&=x-1)
        if(w[x]) if(gmx(f,w[x]+1)) g=W[x];
        else if(w[x]+1==f) g+=W[x];
}
inline void clr(int x){
    for(;x<=m;x+=x&-x) W[x]=w[x]=0;
}
inline void cdq(int l,int r){
    if(l==r){ if(gmx(s[l].f[T],1)) s[l].g[T]=1; return; }
    int m=l+r>>1,i,j,t;
    memcpy(b+l,s+l,(r-l+1)*sizeof(P));
    for(t=l,i=l,j=m+1;t<=r;)
        if(b[t].r<=m) s[i++]=b[t++];
            else s[j++]=b[t++];
    cdq(l,m);
    for(t=i=l,j=m+1;i<=m && j<=r;)
        if(s[i].v>=s[j].v) add(s[i++]);else query(s[j++]);
    for(;j<=r;) query(s[j++]);
    for(i=l;i<=m;++i) clr(s[i].h);
    cdq(m+1,r);
    for(t=i=l,j=m+1;i<=m && j<=r;)
        if(s[i].v>=s[j].v) b[t++]=s[i++]; else b[t++]=s[j++];
    for(;i<=m;) b[t++]=s[i++];
    for(;j<=r;) b[t++]=s[j++];
    for(;l<=r;++l) s[l]=b[l];
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%d%d",&s[i].v,&s[i].h),s[i].r=i;
    for(int i=1;i<=n;++i) w[i]=s[i].h; 
    sort(w+1,w+1+n); m=unique(w+1,w+1+n)-w-1;
    for(int i=1;i<=n;++i) s[i].h=m-(lower_bound(w+1,w+1+m,s[i].h)-w)+1;
    sort(s+1,s+1+n,cv); memset(w,0,sizeof w);
    cdq(1,n);
    for(int i=1;i<=n;++i){
        s[i].r=n-s[i].r+1;
        s[i].h=m-s[i].h+1; s[i].v=-s[i].v;
    }
    sort(s+1,s+1+n,cv); ++T;
    cdq(1,n); db S=0; int A=0;
    for(int i=1;i<=n;++i)
        if(gmx(A,s[i].f[0])) S=s[i].g[0];
        else if(A==s[i].f[0]) S+=s[i].g[0];
    printf("%d\n",A);
    for(int i=1;i<=n;++i)
        W[n-s[i].r+1]=(s[i].f[0]+s[i].f[1]==A+1?s[i].g[0]*s[i].g[1]/S:0);
    for(int i=1;i<=n;++i) printf("%lf ",W[i]);
}



D[教义问答手册]

画风突变,变成了纯分治
先令 s[i]=∑Lj=1b[i+j] s [ i ] = ∑ j = 1 L b [ i + j ] 那么问题就变成了选择若干个元素距离>=L
我们先考虑怎么在线做,有两个选择
1.线段树,设每个点存 fi,j f i , j 表示去掉左边i个和右边j个的最大答案,那么可以搞一个转移 fi,j=max{fi,k+f′j,L−1−k},0<=k<=L−1 f i , j = m a x { f i , k + f j , L − 1 − k ′ } , 0 <= k <= L − 1
但是这个复杂度爆炸了 O(nL3) O ( n L 3 ) 所以不行
2.分块,反正也不太行
好的我们开始借鉴一下线段树的做法,考虑分治
我们发现,原来那个O(L^3)的dp可以被化简到O(L)
考虑处理经过中点的询问,我们搞一个 fi,j f i , j 距离中点距离为i,做到j这个位置的最大答案,对左边和右边分别从中点做一次这个dp,那么右边部分转移就是 fj=max{fj−1,fj−L+s[j]} f j = m a x { f j − 1 , f j − L + s [ j ] } ,左边也是类似的
那么对于一个询问[l,r]就是 max{fk,l+fL−k−1,r},0<=k<=L m a x { f k , l + f L − k − 1 , r } , 0 <= k <= L
这样就可以做到 O(L∗n logn) O ( L ∗ n   l o g n ) 了

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 200010
using namespace std;
struct opt{ int l,r,i; } g[N],b[N];
int n,m,c,L,s[N],v[N],f[50][N],A[N];
inline void cdq(int l,int r,int n){
    if(l==r) return;
    int m=l+r>>1,i,j;
    for(int T=0;T<L;++T){
        int*F=f[T];
        F[j=m+1+T]=max(0,s[j]);
        for(++j;j<=m+T+L&&j<=r;++j) F[j]=max(F[j-1],s[j]);
        for(;j<=r;++j) F[j]=max(F[j-1],F[j-L]+s[j]);
        F[i=m-T]=max(0,s[i]);
        for(--i;i>m-T-L&&i>=l;--i) F[i]=max(F[i+1],s[i]);
        for(;i>=l;--i) F[i]=max(F[i+1],F[i+L]+s[i]);
    }
    for(i=1;i<=n;++i){
        if(g[i].l<=m&&m<g[i].r){
            A[g[i].i]=max(f[0][g[i].l],f[0][g[i].r]);
            for(int T=0;T<L;++T) if(T<=m-g[i].l&&L-T<=g[i].r-m)
                A[g[i].i]=max(A[g[i].i],f[T][g[i].l]+f[L-T-1][g[i].r]);
            swap(g[i--],g[n--]);
        }
    }
    for(i=1,j=n;i<=j;++i) if(g[i].r>m) swap(g[i--],g[j--]);
    cdq(l,m,j);
    for(i=1,j=n;i<=j;++i) if(g[i].l<=m) swap(g[i--],g[j--]);
    cdq(m+1,r,j);
}
int main(){
    scanf("%d%d",&n,&L);
    for(int i=1;i<=n;++i){
        scanf("%d",s+i); s[i]+=s[i-1];
    }
    for(int i=n;i>=L;--i) s[i]-=s[i-L];
    scanf("%d",&m);
    for(int l,r,i=1;i<=m;++i){
        scanf("%d%d",&l,&r); l+=L-1;
        if(r-l+1>L) g[++c]=(opt){l,r,i};
        else for(int j=l;j<=r;++j) A[i]=max(A[i],s[j]);
    }
    cdq(L,n,c);
    for(int i=1;i<=m;++i) printf("%d\n",A[i]);
}



E[二分图]

新的题目,新的套路
首先二分图的性质:没有长度为奇数的环
维护这个性质我们使用带权并查集,首先一开始整个图一定是个森林
考虑加入一条边所带来的影响,设v[x]表示x到root[x]所经过的边数的奇偶性
那么两个点a,b的距离就可以用v[a]^v[b]表示
如果v[a]^v[b]是奇数,那么加入边(a,b)就形成了奇环,否则我们可以不加入这条边,因为它不影响构成奇环
那么加上删除操作,我们需要分治,具体就是将这些边的时间视为线段树上的一个区间,让后对每个时间节点进行操作,如果覆盖了整个节点,那么我们将这条边加入并查集,否则递归下去,注意到这里有需要撤销的操作,我们可以用可持久化,但是这里只需要暴力撤销+不要路径压缩就可以了,复杂度 O(nlog2n) O ( n l o g 2 n )

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 200010
using namespace std;
struct edge{ int u,v,s,t; } G[N<<1];
int n,m,T,f[N],v[N],s[N<<2],r[N],t;
inline int g(int x,int& y,int r=0){
    for(y=x;y!=f[y];y=f[y]) r^=v[y];
    return r;
}
inline void merge(int x,int y,int d){
    if(r[x]>r[y]) swap(x,y);
    if(r[x]==r[y]){ ++r[y]; s[++t]=-y; }
    f[x]=y; s[++t]=x; v[x]=d;
}
inline void back(int dt){
    for(;t>dt;--t)
        s[t]<0?--r[-s[t]]:f[s[t]]=s[t],v[s[t]]=0;
}
inline void cdq(int l,int r,int n){
    int m=l+r>>1,i,j,dt=t;
    for(i=1;i<=n;++i){
        if(G[i].s<=l && r<=G[i].t){
            int x,y,d=g(G[i].u,x)^g(G[i].v,y)^1;
            if(x!=y) merge(x,y,d);
            else if(d){
                for(;l<=r;++l) puts("No");
                back(dt); return;
            }
            swap(G[i--],G[n--]); 
        }
    }
    if(l==r){ puts("Yes"); back(dt); return; }
    for(i=1,j=n;i<=j;++i)
        if(G[i].s>m) swap(G[i--],G[j--]);
    cdq(l,m,j);
    for(i=1,j=n;i<=j;++i)
        if(G[i].t<=m) swap(G[i--],G[j--]);
    cdq(m+1,r,j);
    back(dt);
}
int main(){
    scanf("%d%d%d",&n,&m,&T);
    for(int i=1;i<=n;++i) f[i]=i;
    for(int i=1;i<=m;++i){
        scanf("%d%d%d%d",&G[i].u,&G[i].v,&G[i].s,&G[i].t);
        if(++G[i].s>G[i].t){ --m; --i; }
    }
    cdq(1,T,m);
}



F[Shortest Path Queries]

和上面的题目套路一样辣
注意到题目要求的是异或最短路
那么我们考虑一下怎么做,先搞个dfs树出来,加上返祖边就得到了很多环
令 vx=vfx xor w(fx,x) v x = v f x   x o r   w ( f x , x ) ,我们发现这个最短路其实就是 vx xor vy v x   x o r   v y 再异或上若干个环的权值,因为走到环上和走回来的权值抵消了,那么就可以愉快地使用线性基了

#include<map>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 400010
using namespace std;
map<pair<int,int>,int> S;
struct Q{ int x,y; } g[N<<2];
struct edge{ int u,v,c,s,t; } G[N<<2];
int n,m,cnt,qc,qn,clk,f[N],v[N],s[N<<2],t,r[N];
struct Lbase{
    int p[31];
    Lbase(){ memset(p,0,sizeof p); }
    inline void add(int x){
        for(int i=30;~i;--i)
            if(x&(1<<i))
                if(!p[i]){ p[i]=x; return; }
                else x^=p[i];
    }
    inline int query(int x){
        for(int i=30;~i;--i) if(x>(x^p[i])) x^=p[i];
        return x;
    }
} Z;
inline int gf(int x,int& y,int r=0){
    for(y=x;y!=f[y];y=f[y]) r^=v[y];
    return r;
}
inline void merge(int x,int y,int d){
    if(r[x]>r[y]) swap(x,y);
    if(r[x]==r[y]){ ++r[y]; s[++t]=-y; }
    f[x]=y; s[++t]=x; v[x]=d;
}
inline void back(int dt){
    for(;t>dt;--t)
        s[t]<0?--r[-s[t]]:f[s[t]]=s[t],v[s[t]]=0;
}
inline void cdq(int l,int r,int n,Lbase B){
    int dt=t,m=l+r>>1,i,j;
    for(int i=1;i<=n;++i)
        if(G[i].s<=l && r<=G[i].t){
            int x,y,d=gf(G[i].u,x)^gf(G[i].v,y);
            if(x!=y) merge(x,y,G[i].c^d);
            else B.add(d^G[i].c);
            swap(G[i--],G[n--]);
        }
    if(l==r){
        printf("%d\n",B.query(gf(g[l].x,j)^gf(g[l].y,j)));
        back(dt); return;
    }
    for(i=1,j=n;i<=j;++i)
        if(G[i].s>m) swap(G[i--],G[j--]);
    cdq(l,m,j,B);
    for(i=1,j=n;i<=j;++i)
        if(G[i].t<=m) swap(G[i--],G[j--]);
    cdq(m+1,r,j,B); back(dt);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) f[i]=i; 
    for(int x,y,c,i=1;i<=m;++i){
        scanf("%d%d%d",&x,&y,&c);
        G[++cnt]=(edge){x,y,c,1,-1};
        S[make_pair(x,y)]=cnt;
    }
    scanf("%d",&m);
    for(int o,x,y,c,i=1;i<=m;++i){
        scanf("%d%d%d",&o,&x,&y);
        if(o==1){
            scanf("%d",&c);
            G[++cnt]=(edge){x,y,c,clk+1,-1};
            S[make_pair(x,y)]=cnt;
        } else if(o==2){
            G[S[make_pair(x,y)]].t=clk;
            S.erase(make_pair(x,y));
        } else{ g[++clk]=(Q){x,y}; }
    }
    for(int i=1;i<=cnt;++i) if(!~G[i].t) G[i].t=clk;
    cdq(1,clk,cnt,Z);
}



G[Radio stations]

画风又变回来了, ri r i 范围巨大+需要数值运算强制不给你离散化只好分治辣

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100010
using namespace std;
int n,k,m=0,x[N],f[N],r[N],w[N],V,sa[N]; long long ans=0;
struct opt{
    int o,x,l;
} s[N<<2],b[N<<2];
inline bool c1(int i,int j){ return r[i]>r[j]; }
inline void cdq(int l,int r){
    if(l==r) return;
    int m=l+r>>1,i=l,j=m+1,t=l;
    cdq(l,m);
    cdq(m+1,r);
    for(int c;i<=m && j<=r;){
        if(s[i].x<=s[j].x){
            if(!s[i].o)
                for(int x=s[i].l;x<=V;x+=x&-x) ++w[x];
            b[t++]=s[i++];
        } else {
            if(s[j].o){
                c=0;
                for(int x=min(V,s[j].l+k);x;x&=x-1) c+=w[x];
                for(int x=max(0,s[j].l-k-1);x;x&=x-1) c-=w[x];
                ans+=(s[j].o==2?c:-c);
            }
            b[t++]=s[j++];
        }
    }
    for(;i<=m;) b[t++]=s[i++];
    for(int c;j<=r;){
        if(s[j].o){
            c=0;
            for(int x=min(V,s[j].l+k);x;x&=x-1) c+=w[x];
            for(int x=max(0,s[j].l-k-1);x;x&=x-1) c-=w[x];
            ans+=(s[j].o==2?c:-c);
        }
        b[t++]=s[j++];
    }
    for(;l<=r;++l){
        if(!s[l].o) for(int x=s[l].l;x<=V;x+=x&-x) w[x]=0;
        s[l]=b[l];
    }
}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i)
        scanf("%d%d%d",x+i,r+i,f+i),sa[i]=i;
    sort(sa+1,sa+1+n,c1);
    for(int i,j=1;j<=n;++j){ i=sa[j];
        s[++m]=(opt){0,x[i],f[i]}; V=max(V,f[i]);
        s[++m]=(opt){1,x[i]-r[i]-1,f[i]};
        s[++m]=(opt){2,x[i]+r[i],f[i]};
    }
    cdq(1,m);
    printf("%lld\n",ans-n);
}



H[k大数查询]

画风突变为整体二分
好难啊不会啊打个树套树水过去吧
外面权值,里面区间加打个标记就可以了
求答案就在外面二分,询问[l,r]的元素个数而已

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 50010
#define LL long long
#define mid (l+r>>1)
using namespace std;
int n,m,cnt=0,rt[N<<3];
struct tree{ int l,r,t; LL s; } s[N*400];
inline void add(int l,int r,int& x,int L,int R){
    if(!x) x=++cnt;
    if(L<=l && r<=R){ s[x].t++; s[x].s+=r-l+1; return; }
    if(L<=mid) add(l,mid,s[x].l,L,R);
    if(mid<R) add(mid+1,r,s[x].r,L,R);
    s[x].s=s[s[x].l].s+s[s[x].r].s+s[x].t*(r-l+1);
}
inline LL sum(int l,int r,int& x,int L,int R,int T=0){
    if(!x) x=++cnt;
    if(L<=l && r<=R) return s[x].s+T*(r-l+1ll);
    int A=0;
    if(L<=mid) A+=sum(l,mid,s[x].l,L,R,T+s[x].t);
    if(mid<R) A+=sum(mid+1,r,s[x].r,L,R,T+s[x].t);
    return A;
}
inline void update(int l,int r,int x,int L,int R,int c){
    add(1,n,rt[x],L,R);
    if(l==r) return;
    if(c<=mid) update(l,mid,x<<1,L,R,c);
    else update(mid+1,r,x<<1|1,L,R,c);
}
inline int query(int l,int r,int x,int L,int R,int k){
    if(l==r) return l;
    LL c=sum(1,n,rt[x<<1|1],L,R);
    if(c>=k) return query(mid+1,r,x<<1|1,L,R,k);
    else return query(l,mid,x<<1,L,R,k-c);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int o,l,r,c;m--;){
        scanf("%d%d%d%d",&o,&l,&r,&c);
        if(o==1) update(0,n,1,l,r,c);
        else printf("%d\n",query(0,n,1,l,r,c));
    }
}



I[网络管理network]

树上第k大?很简单啊树剖套BIT套主席树,四个log随便过
考虑静态的做法,每个节点维护它到根的权值线段树,那么询问就可以二分了
单独考虑修改,我们先求出dfs序,让后一次修改相当于整个子树的加减操作,询问只是一个点询问,那么搞一个BIT套主席树就可以了,复杂度直接降到了2个log

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 80010
#define mid (l+r>>1)
using namespace std;
int n,m,q,w[N<<1],h[N],rt[N],cnt,v[N],clk,V[N];
int f[N],d[N],top[N],son[N],sz[N],l[N],r[N],o[N][3];
struct edge{ int v,nt; } G[N<<1];
struct tree{ int l,r,s; } s[N<<7];
inline void copy(int l,int r,int r1,int& r2,int k){
    if(!r2) r2=++cnt; s[r2].s=s[r1].s+1;
    if(l==r) return;
    if(k<=mid){ s[r2].r=s[r1].r; copy(l,mid,s[r1].l,s[r2].l,k); }
        else{ s[r2].l=s[r1].l; copy(mid+1,r,s[r1].r,s[r2].r,k); }
}
inline void insert(int l,int r,int& x,int k,int c){
    if(!x) x=++cnt; s[x].s+=c;
    if(l==r) return;
    if(k<=mid) insert(l,mid,s[x].l,k,c);
        else insert(mid+1,r,s[x].r,k,c);
}
struct fenwick{
    int w[N];
    inline void add(int x,int v,int c){
        for(;x<=n;x+=x&-x) insert(1,m,w[x],v,c);
    }
    inline void query(int x,int* t){
        if(x) t[++*t]=rt[x]; x=l[x];
        for(;x;x&=x-1) if(w[x]) t[++*t]=w[x]; 
    }
} T;
inline void dfs(int x,int p){
    f[x]=p; d[x]=d[p]+1; sz[x]=1;
    for(int v,i=h[x];i;i=G[i].nt)
        if(!d[v=G[i].v]){
            dfs(v,x);
            sz[x]+=sz[v];
            if(sz[son[x]]<sz[v]) son[x]=v;
        }
}
inline void dgs(int x,int p){
    l[x]=++clk; top[x]=p;
    copy(1,m,rt[f[x]],rt[x],v[x]);
    if(son[x]) dgs(son[x],p);
    for(int v,i=h[x];i;i=G[i].nt)
        if(!l[v=G[i].v]) dgs(v,v);
    r[x]=clk;
}
inline int gLca(int x,int y){
    for(;top[x]!=top[y];y=f[top[y]])
        if(d[top[x]]>d[top[y]]) swap(x,y);
    return d[x]>d[y]?y:x;
}
int A[140],B[140];
inline int query(int l,int r,int k){
    if(l==r) return l;
    int C=0;
    for(int i=1;i<=*A;++i) C+=s[s[A[i]].r].s;
    for(int i=1;i<=*B;++i) C-=s[s[B[i]].r].s;
    if(C>=k){
        for(int i=1;i<=*A;++i) A[i]=s[A[i]].r;
        for(int i=1;i<=*B;++i) B[i]=s[B[i]].r;
        return query(mid+1,r,k);
    } else {
        for(int i=1;i<=*A;++i) A[i]=s[A[i]].l;
        for(int i=1;i<=*B;++i) B[i]=s[B[i]].l;
        return query(l,mid,k-C);
    }
}   
int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;++i) scanf("%d",v+i);
    memcpy(w+1,v+1,n<<2);
    for(int x,y,i=1;i<n;++i){
        scanf("%d%d",&x,&y);
        G[++m]=(edge){y,h[x]}; h[x]=m;
        G[++m]=(edge){x,h[y]}; h[y]=m;
    }
    m=n;
    for(int i=1;i<=q;++i){
        scanf("%d%d%d",o[i],o[i]+1,o[i]+2);
        if(!o[i][0]) w[++m]=o[i][2];
    }
    sort(w+1,w+1+m); m=unique(w+1,w+1+m)-w-1;
    for(int i=1;i<=n;++i) v[i]=lower_bound(w+1,w+1+m,v[i])-w;
    for(int i=1;i<=q;++i) if(!o[i][0]) o[i][2]=lower_bound(w+1,w+1+m,o[i][2])-w;
    dfs(1,0);
    dgs(1,1);
    for(int x,y,t,i=1;i<=q;++i){
        if(!o[i][0]){
            x=o[i][1];
            T.add(l[x],v[x],-1);
            T.add(r[x]+1,v[x],1);
            v[x]=o[i][2];
            T.add(l[x],v[x],1);
            T.add(r[x]+1,v[x],-1);
        } else {
            x=o[i][1]; y=o[i][2]; t=gLca(x,y);
            if(d[x]+d[y]-d[t]-d[f[t]]<o[i][0]){
                puts("invalid request!");
                continue;
            }
            *A=*B=0;
            T.query(x,A);
            T.query(y,A);
            T.query(t,B);
            T.query(f[t],B);
            printf("%d\n",w[query(1,m,o[i][0])]);
        }
    }
}
CDQ分治与整体二分 前言 CDQ分治和整体二分都是离线算法,并且都是基于分治的思想。大部分非强制在线的树套树题都可以使用整体二分或CDQ分治解决,当然也有一些是不行的,比如动态求区间种类数或者动态求区间 mexmexmex。 CDQ 分治 分治 首先要了解什么是分治,分治的全称为分而治之,英文名为 divide&nbsp;and&nbsp;conquerdivide\ and\ conquerdivide&nbsp;and&nbsp;conquer,和分治相关的算法有很多,譬如线段树,点分治,归并排序等。下面以归并排序为例。 阅读详情

相关推荐

hdu 1195 Open the Lock(bbfs(双向广搜))

<br />第一次使用双向广搜算法求解,感觉效率的确提高了不止两倍。刚开始的一直WA,后来发现是每次都要扩展一层的节点,然后在扩展另一层的节点,而不是每次只扩展一个节点。<br /> /* Author: ACb0y Date: 2010-9-21 Type: bbfs(双向广搜) ProblemId: hdu 1195 Open the Lock Result: AC */ #include <iostream> #include <queue> using namespace

个人独立博客http://www.acb0y.cn/ 1526

CDQ 分治与整体二分

CDQ 分治与整体二分 CDQ 分治 主要是一种分治思想,常用于解决偏序问题。 例如三维偏序问题,我们采用的方法是先处理以第一关键字为区分的左区间、右区间内的答案,再处理左右区间互不干涉的答案。 四维偏序呢? 咕咕咕 整体二分 主要内容 如果题目问我们第 \(k\) 大、最小的最大、最大的最小等问题,我们通常使用二分来解决。 但是如果有多组询问,每次询问都需要使用二分,就会变得非常慢~,所以有了...

EricQian的博客 183

三维重建之条纹投影结构光(三)——相位展开优化与多频外差实践

本文深入探讨了三维重建中条纹投影结构光技术的核心环节——相位展开优化。针对传统路径跟踪法存在的误差传播与路径依赖问题,重点解析了多频外差法的原理与优势,并通过详细的C++代码实践,展示了如何利用三频外差法实现鲁棒、高精度的相位展开,从而获得连续的绝对相位图,为后续三维点云生成奠定坚实基础。

weixin_29074295的博客 529

cdq分治模板

#include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x

snowy_smile的博客 1503

开关电源辅助电源设计全流程拆解

地类型定义包含电路功率地(PGND)高压侧地,与主功率电路地相连整流桥负极、原边电感地、UC3842的功率地信号地(SGND)低压侧地,与控制/采样电路地相连副边整流管地、TL431地、MCU/运放地。

lyzh971103的博客 1468

手把手教你用示波器搞定PCIe 5.0 REFCLK时钟信号测量(附完整参数解读)

本文详细介绍了如何使用示波器进行PCIe 5.0 REFCLK时钟信号测量,包括示波器与探头的选择、关键参数的测量方法及解读,以及实战中的常见问题与解决方案。通过手把手教学和完整参数解读,帮助硬件工程师高效完成时钟信号测量,确保系统稳定性。

weixin_28704565的博客 358

51nod 1376 最长递增子序列的数量(dp、CDQ分治 | BIT)

题意: N≤5×104的序列,0≤Ai≤109,求LIS的数量N\le5\times10^4的序列, 0\le A_i\le 10^9, 求LIS的数量 分析: f[i]:以第i个数结尾的LIS的长度,和该长度的LIS数量f[i]:以第i个数结尾的LIS的长度, 和 该长度的LIS数量 转移的话,显然f[i].first=max{f[j].first}+1,j<i且a[j]<a[i]转

TaoSama's Microcosm 1922

CANFD升级踩坑实录:DBC转换时DLC大于8的信号怎么处理?

本文详细探讨了CANFD升级过程中DLC大于8的信号处理问题,提供了工程化解决方案和AutoSar BSW层配置要点。通过分析CAN与CANFD的DLC本质差异,提出信号拆分重组、DLC重映射和协议层适配三种策略,并给出验证与测试方案,帮助开发者避免数据截断和通信故障。

weixin_42521843的博客 228

[CDQ分治与整体二分]个人对CDQ分治与整体二分的理解

CDQ分治 整体二分

srand(time(0)) 1万+

【全球人口数据集】全球人口密度数据集GPWv4

全球人口密度数据集GPWv4

WW、forever的博客 1002

C++软件调试与异常排查从入门到精通专栏介绍与文章汇总

根据近几年排查软件异常的实践与经验,系统地讲解了C++软件异常常见原因与常用排查方法,以图文并茂的方式给出具体的分析实例,带领大家逐步掌握C++软件异常排查的相关技术与要领。

dvlinker的技术专栏 23万+

整体二分及cdq分治学习小结

整体二分 基本思想 听上去十分的高大上,但是究其本质就是二分答案加强版,对有些题目,我们单次二分答案的代价可能无法做到O(logn)O(log⁡n)O(\log n)。但是对于每个询问它做的操作是几乎相同的,我们认为这类询问是可以合并在一起做的,那么大可不必对于每个询问都去二分答案,我们可以对询问进行分类,划到一个区间去再进行二分答案。 可以看一看2013年XHR的论文。要注意,分了区间...

Rayment的博客 1632

eNSP中玩转Python自动化——解锁网工新姿势(一)

Python自动化一、安装Paramiko模块二、搭建实验环境1、桥接电脑网卡2、配置交换机IP地址交换机配置测试ensp的交换机与电脑的连通性:交换机配置SSHXshell验证三、Python脚本1、执行python脚本2、同步成功 一、安装Paramiko模块 首先安装Python3 然后安装pip解释器 最后安装Paramiko模块 Windows+R打开Cmd,输入命令: pip3 install paramiko 进入python,导入 paramiko模块: import paramiko

LiBai'S BLOG 2万+

cdq分治和整体二分

cdq分治和整体二分的简单应用。

ZigZagK的博客 1703

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

CDQ分治与整体二分小结

前言   这是一波强行总结。   下面是一波瞎比比。   这几天做了几道CDQ/整体二分,感觉自己做题速度好慢啊。   很多很显然的东西都看不出来 分治分不出来 打不出来 调不对   上午下午晚上的效率完全不一样啊。   完蛋.jpg 绝望.jpg。   关于CDQ分治   CDQ分治,求的是三维偏序问题都知道的。   求法呢,就是在分治外面先把一维变成有序   然后

lishufen123a的博客 193

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

点分治+CDQ分治+整体二分全纪录

点分治 点分治讲解 解决树上路径问题 经典例题:点分治(长度小于m的路径计数) 经典例题:点分治(聪聪可可) 经典例题:点分治(多个定值路径计数) 经典例题:点分治(采药) 经典例题:点分治+ST表+优先队列 经典例题:点分治+FFT+概率期望 经典例题:点分治+01分数规划 舒老师推荐点分治(难) 注意:点分治的常数比较大 每个点都会作为重心,然而每个点的遍历次数不止一次...

Coco_T的博客 1492

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Jzoj3467 最长上升子序列

维护一个序列,使它可以进行下面两种操作: 1.在末尾添加一个数字x 2.将整个序列变成第x次操作后的样子 在每次操作后,输出当前序列的最长上升子序列的长度 序列初始时为空 嗯,可持久化线段树的裸题 额这可是noip提高组难度的题,我们发现所有操作可以变成一棵树(离线做法老套路了) 让后每次最多修改数组上的一个值(考虑二分求LIS的过程)最后改回去就好了 注意dfs又卡栈(jzo

扩展の灰(Extended Ash/Cooevjnz) 3022

Jzoj4210 我才不是萝莉控呢

小Y:“小R你是萝莉控吗。”小R:“...”  为了避免这个尴尬的话题,小R决定给小Y做一道题。 有一个长度为n的正整数数组A,满足艾> =艾+ 1,现在构造一个数组B,令的Bi = ΣA[j] (j∈[i,n])。 现在,有一个N * N的网格图,左下角坐标是(1,1),右上角坐标是(N,N)。有一个小SB正在坐标为(n,1)的位置,每一时刻,如果他现在在(x,y),他可以选择走到(x?-

扩展の灰(Extended Ash/Cooevjnz) 1772

LOJ 6495~6497「雅礼集训 2018 Day1」

. 真是看懂std就秒的三道题,但是看懂就很难啊 (感谢superguymj和memset0两个大佬的代码) 稍微说一下题解 -------------------------------------------------------- 「雅礼集训 2018 Day1」树 这道题的正解被superguymj大佬吊打了,原来O(n⋅2n)O(n·2^n)O(n⋅2n)变成了O(n4)O(n^4)...

扩展の灰(Extended Ash/Cooevjnz) 1562

Jzoj4876 基因突变(红警系列)

邪恶的707刚刚从白垩纪穿越回来,心中产生了一个念头:我要统治人类!     但是统治人类是很庞大且复杂的一个工程,707尝试了洗脑,催眠,以及武装镇压都没能成功地统治人类,于是她决定从科学上对人类的基因进行研究从而达到他的目的。     707获取了人类的基因信息并尝试对基因进行实验。他发现可以把人类的基因看做一个只包含小写字母的字符串,并定义从头开始任意长度的基因为“源头基因”人类身上与源

扩展の灰(Extended Ash/Cooevjnz) 1436

Jzoj5235 好的排列

对于一个1->n的排列 ,定义A中的一个位置i是好的,当且仅当Ai-1>Ai 或者Ai+1>Ai。对于一个排列A,假如有不少于k个位置是好的,那么称A是一个好的排列。 现在有q个询问,每个询问给定n,k,问有多少排列是好的。答案对10^9+7取模。 显然是计数类dp,我们设f[i][j]表示对于一个1->i的排列,好的位置有j个的情况 考虑转移,显然f[i][j]->f[i+1][k]相

扩展の灰(Extended Ash/Cooevjnz) 1289

Jzoj4877 力场护盾(红警系列)

ZMiG成功粉碎了707的基因突变计划,为了人类的安全,他决定向707的科学实验室发起进攻!707并没有想到有人敢攻击她的实验室,一时间不知所措,决定牺牲电力来换取自己实验室的平安。     在实验室周围瞬间产生了一个无限大的力场护盾,它看上去无懈可击!不过ZMiG拥有惊人的双向观察能力,经过他的反复观察,找到了这个护盾的N个弱点,他本想逐一击破,却发现一股神秘力量阻止了他的行为。原来他身处力场

扩展の灰(Extended Ash/Cooevjnz) 1169

Jzoj3805 小X的二叉堆计数

题意:给你n个不同的数问你能构成多少个不同的二叉堆 显然不能枚举,我们考虑用递推 我们令f[i]表示以i为根的二叉堆有多少种 令l,r为i的左右儿子,令size[i]为以i为根的堆的大小 那么显然,f[i]=f[l]*f[r]*C(size[i]-1,size[l]),因为n个数互不相同,所以没有重复 相当于是1~size[i]这几个数填进这个堆中,那么显然根只能填最大那个,让后在剩下的

扩展の灰(Extended Ash/Cooevjnz) 888

PKUSC2018部分题解

. LOJ的std+数据+当时讲题的记忆硬是刚了五道题(D1t3咕咕咕) 稍微写一下题解 LOJ6432 「PKUSC2018」真实排名 一道比较简单的题,不过需要注意很多细节 考虑排名不变的两种情况 1.自己分数xxx不变,那么所有分数在[[x2],x)[[\frac{x}{2}],x)[[2x​],x)中的其他人的分数也不能变,否则必然有在自己后面的人到自己前面 2.自己分数xxx翻倍,那么...

扩展の灰(Extended Ash/Cooevjnz) 848
上一篇: E文本加密器0.2 Aplha版
下一篇: 高斯消元模板
扩展的灰
博客等级 码龄12年 60粉丝 342原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值