【知识点】分治相关算法

整体二分:

对于一类要求支持离线操作和询问达成某条件的操作次数的问题,可以把所有询问扔到一起二分答案。

具体地,每次对于操作区间$[l,r]$,将位于$[l,mid]$之间的操作做掉,然后依次判断答案位于$[l,r]$之间的每个询问的条件达没达成。

如果达成则该询问的答案$leq mid$,扔到操作区间$[l,mid]$;否则该询问的答案$>mid$,扔到操作区间$[mid+1,r]$。

如果$l=r$则更新这些询问的答案,否则清空操作并继续递归左右两边的操作区间。

复杂度$O(nlog{n} imes 操作复杂度)$。

#include<bits/stdc++.h>
#define maxn 300005
#define maxm 500005
#define inf 0x7fffffff
#define ll unsigned long long
#define rint register ll
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
ll P[maxn],L[maxn],R[maxn],n,m;
ll C[maxn<<1],A[maxn],ans[maxn]; 
vector<ll> nd[maxn],pos[maxn];
ll tp1[maxn],tp2[maxn];

inline ll read(){
    ll x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

inline ll lowbit(ll x){return x&(-x);}
inline void add(ll x,ll y){for(ll i=x;i<=2*m;i+=lowbit(i))C[i]+=y;}
inline ll qry(ll x){ll res=0;for(ll i=x;i;i-=lowbit(i))res+=C[i];return res;}

inline void solve(ll l,ll r){
    ll mid=l+r>>1,tot1=0,tot2=0;
    for(rint i=l;i<=mid;i++) add(L[i],A[i]),add(R[i]+1,-A[i]);
    for(rint i=0;i<nd[l].size();i++){
        rint u=nd[l][i],res=0;
        for(rint j=0;j<pos[u].size();j++) res+=qry(pos[u][j]);
        if(l==r) (res>=P[u])?(ans[u]=l):(ans[u]=-1);
        else{
            if(res>=P[u]) tp1[++tot1]=u;
            else P[u]-=res,tp2[++tot2]=u;
        }
    } 
    nd[l].clear();
    for(rint i=l;i<=mid;i++) add(L[i],-A[i]),add(R[i]+1,A[i]);
    if(l!=r){
        for(rint i=1;i<=tot1;i++) nd[l].push_back(tp1[i]);
        for(rint i=1;i<=tot2;i++) nd[mid+1].push_back(tp2[i]);
        solve(l,mid),solve(mid+1,r);
    }
}

int main(){
    n=read(),m=read();
    for(rint i=1;i<=m;i++)
        {ll x=read();pos[x].push_back(i),pos[x].push_back(i+m);}
    for(rint i=1;i<=n;i++) P[i]=read();
    ll k=read();
    for(rint i=1;i<=k;i++){
        L[i]=read(),R[i]=read(),A[i]=read();
        if(R[i]<L[i]) R[i]+=m;
    }
    for(rint i=1;i<=n;i++) nd[1].push_back(i); 
    solve(1,k);
    for(rint i=1;i<=n;i++){
        if(ans[i]==-1) printf("NIE
");
        else printf("%lld
",ans[i]);
    }
    return 0;
}
整体二分

CDQ分治:

对于一类子区间之间会产生影响的分治问题,可以在归并时处理左边对右边的影响。

一般用来解三维偏序,大概是第一维$sort$,第二维归并,第三维数据结构来做。

复杂度$O(nlog{n} imes 操作复杂度)$。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 100005
#define maxm 200005
#define inf 0x7fffffff
#define ll long long

using namespace std;
int N,K,cnt,f[maxn],sum[maxm];
struct node{
    int a,b,c,w,ans;
}e[maxn],t[maxn],t1[maxn],t2[maxn],t3[maxn];

inline int read(){
    int x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

inline bool cmp(node x,node y){
    if(x.a==y.a && x.b==y.b) return x.c<y.c;
    else if(x.a==y.a) return x.b<y.b;
    else return x.a<y.a;
}
inline int lowbit(int x){return x&-x;}
inline int query(int x){int s=0; for(int i=x;i;i-=lowbit(i)) s+=sum[i];return s;}
inline void add(int x,int y){for(int i=x;i<=K;i+=lowbit(i)) sum[i]+=y;}
inline bool check(int i,int j){return t[i].a==t[j].a && t[i].b==t[j].b && t[i].c==t[j].c;}

inline void cdq(int l,int r){
    if(l==r) return;
    int mid=l+r>>1;
    cdq(l,mid),cdq(mid+1,r);
    int p0=0,p1=0,p2=0,p3=0;
    for(int i=l;i<=mid;i++) t1[++p1]=e[i];
    for(int i=mid+1;i<=r;i++) t2[++p2]=e[i];
    for(int i=1;i<=p2;i++){
        while(t1[p0+1].b<=t2[i].b && p0<p1) t3[++p3]=t1[++p0],add(t3[p3].c,t3[p3].w);
        t3[++p3]=t2[i],t3[p3].ans+=query(t3[p3].c);
    }
    for(int i=p0+1;i<=p1;i++) t3[++p3]=t1[i];
    for(int i=l;i<=r;i++) e[i]=t3[i-l+1];
    for(int i=1;i<=p0;i++) add(t1[i].c,-t1[i].w);
    return;
}

int main(){
    cnt=read(),K=read(); 
    for(int i=1;i<=cnt;i++) t[i].a=read(),t[i].b=read(),t[i].c=read();
    sort(t+1,t+1+cnt,cmp); 
    for(int i=1;i<=cnt;){
        e[++N]=t[i]; int j=i;
        while(check(i,j)&&j<=cnt) j++;
        e[N].w=j-i,i=j;
    }
    cdq(1,N); for(int i=1;i<=N;i++) f[e[i].ans+e[i].w-1]+=e[i].w;
    for(int i=0;i<cnt;i++) printf("%d
",f[i]);
    return 0; 
}
CDQ分治

三分:

寻找单峰函数的极值。注意整数三分的下界是$r-l+1geq 4$。

复杂度$O(nlog{n})$。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>

using namespace std;
#define MAXN 100005
#define MAXM 500005
#define INF 0x7fffffff
#define ll long long
#define eps 1e-6

double A[MAXN];int N;

inline int read(){
    int x=0,f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar())
        if(c=='-')
            f=-1;
    for(;isdigit(c);c=getchar())
        x=x*10+c-'0';
    return x*f;
}

inline double power(double a,int b){
    double ans=1.0;
    while(b){
        if(b&1) ans*=a;
        a*=a,b>>=1;
    }return ans;
}

inline double gety(double x){
    double ans=0.0;
    for(int i=N;i>=0;i--)
        ans+=A[i]*power(x,i);
    return ans;
}

int main(){
    N=read();double l,r;
    scanf("%lf%lf",&l,&r);
    for(int i=N;i>=0;i--)
        scanf("%lf",&A[i]);
    while(l+eps<=r){
        //cout<<l<<" "<<r<<endl; 
        double t1=l+((r-l)/3.0);
        double t2=r-((r-l)/3.0);
        if(gety(t1)>gety(t2)) r=t2;
        else l=t1;
    }
    printf("%.5lf
",l);
    return 0;
}
三分法
原文地址:https://www.cnblogs.com/YSFAC/p/13198896.html