• 【知识点】分治相关算法


    整体二分:

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

    具体地,每次对于操作区间$[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;
    }
    三分法
  • 相关阅读:
    [hdu3853]LOOPS(概率dp)
    [poj2096]Collecting Bugs(概率dp)
    lintcode-42-最大子数组 II
    lintcode-39-恢复旋转排序数组
    lintcode-36-翻转链表 II
    lintcode-34-N皇后问题 II
    lintcode-33-N皇后问题
    lintcode-32-最小子串覆盖
    lintcode-31-数组划分
    lintcode-30-插入区间
  • 原文地址:https://www.cnblogs.com/YSFAC/p/13198896.html
Copyright © 2020-2023  润新知