CDQ分治&整体二分九连测


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

A[适者]

先来一个不是cdq分治的题(虽然也可以用分治做)
首先先来考虑按照什么顺序来搞掉这些机甲
对于两个相邻的机甲i,j,先i后j的代价是 A i D i + ( D i + D j ) A j
那么如果i应该比j先消灭就有
A i D i + ( D i + D j ) A j < A j D j + ( D i + D j ) A i
也就是 A j D i < D j A i
所以将原序列按照 D i / A i 做关键字排序
让后考虑怎么用这两个大招
我们设 C i 表示干掉第i个人的收益
那么可以得出 C i = A i ( j = 1 i D j ) + D i ( j = i + 1 n A j )
那么,同时干掉两个人i,j(i<j)的收益就是 C i + C j A i D j
我们用斜率dp来解决这个问题,设干掉的第二个人是i时的最大收益为 f i
得到转移式子 f i = m a x { C i + C j A j D i }
化简一下
C j = A j D i f i + C i
发现凸包上面的点的x坐标 ( A j ) 不是单调的,而且询问的斜率 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)的个数
那我们考虑一下怎么做
首先我们将所有的花按照 a i 排序,那么就只有前面i的能为后面的j给出贡献
让后,如果没有 c i 这个限制就可以用树状数组统计 b i < b j 的个数了
现在加上c的限制,我们考虑分治
将整个序列分为[l,mid]和[mid+1,r]
让后分别按照b作为关键字排序
接下来,类似归并排序的操作,我们将这两个部分重新合并到一起,设i,j分别为两个部分未处理的第一个元素
b i < b j 我们将 c i 加入BIT,否则我们询问BIT中有多少个小于 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中出现的条件是什么
我们设 f i 表示以i结尾的LIS的长度,那么转移很简单 f i = m a x ( f j + 1 ) , v j < v i , h j < h i
让后我们将整个序列取反并翻转做一次LIS就得到了 f i
如果有 f i + f i = | L I S | + 1 那么i就可以出现在LIS之中
另外,i被拦截的概率就是通过i的方案数/总方案数
我们设 g i , g i 表示正着做和倒着做的方案数,那么概率就是 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 ] = j = 1 L b [ i + j ] 那么问题就变成了选择若干个元素距离>=L
我们先考虑怎么在线做,有两个选择
1.线段树,设每个点存 f i , j 表示去掉左边i个和右边j个的最大答案,那么可以搞一个转移 f i , j = m a x { f i , k + f j , L 1 k } , 0 <= k <= L 1
但是这个复杂度爆炸了 O ( n L 3 ) 所以不行
2.分块,反正也不太行
好的我们开始借鉴一下线段树的做法,考虑分治
我们发现,原来那个O(L^3)的dp可以被化简到O(L)
考虑处理经过中点的询问,我们搞一个 f i , j 距离中点距离为i,做到j这个位置的最大答案,对左边和右边分别从中点做一次这个dp,那么右边部分转移就是 f j = m a x { f j 1 , f j L + s [ j ] } ,左边也是类似的
那么对于一个询问[l,r]就是 m a x { f k , l + f L k 1 , r } , 0 <= k <= L
这样就可以做到 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 ( 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树出来,加上返祖边就得到了很多环
v x = v f x   x o r   w ( f x , x ) ,我们发现这个最短路其实就是 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]

画风又变回来了, 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])]);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/JacaJava/article/details/81841414