• cdq分治(陈丹琦分治)


    //转自:http://blog.csdn.net/snowy_smile/article/details/49668689
    /*
    算法介绍之cdq分治:
    
    其实cdq分治的思想与应用都能被很简单地描述——它是用来解决各种区间段转移问题[x->y(x<y)]的。
    我们用f[x]表示位置x转移之后的结果,用solve(l,r)来传递完全限制在[l,r]范围内的状态转移,并且转移a->b一定有a<b
    那么对于solve(l,r)
    1,如果l==r,没有后续转移,程序结束。
    2,求出m=(l+r)>>1;
    3,solve(l,m);
    4,传递[l,m]对[m+1,r]的贡献。
    5,solve(m+1,r);
    这样cdq分治就做完啦
    */
    
    /*
    【题意】
    给你一个n(750)*m(750)的棋盘,每个格子(i,j)都有一种颜色c[i][j],
    颜色范围是[1,top]中的整数,top取值范围是[1,5e5]。
    对于一次跳跃A(y1,x1)->B(y2,x2),我们说这次跳跃是合法的,要求必须有:
    1,y2>y1
    2,x2>x1
    3,c[y1][x1]!=c[y2][x2];
    我们想要求出从(1,1)经过若干次跳跃之后到达(n,m)的方案数,mod(1e9+7)
    
    【类型】
    CDQ分治
    
    【分析】
    首先,我们可以构想一个暴力DP:
    用f[i][j]表示从(1,1)到(i,j)的方案数
    那么f[i][j]=∑f[u][v],u<i&&v<j&&c[u][v]!=c[i][j]
    时间复杂度为O(n^2 m^2),想要通过这题,这还差得远。
    
    思考1,
    我们观察到,这道题中,颜色的范围不是1e9,而是5e5。为什么呢?
    我们发现,相比较1e9,5e5这个范围的数组是开得下的,于是我们可以做计数处理,有助于我们维护答案。
    (ps:如果颜色属于1e9范围,因为颜色种类最多只有n*m,所以其实我们可以离散化)
    如果我们用d[x]表示颜色为从(1,1)走到当前处理范围颜色为x的点的方案数,
    并用一个大的变量tot统计从(1,1)走到当前处理范围所有点的方案数之和。
    那么当我们走到一个颜色为x的点时,就可以直接使用 tot-d[x] 来更新到达这个点的方案数。
    这个思想有一定的价值,但是因为题目给出的1,2两个限制条件。
    具体要通过什么途径保证d[]中处理的所有点都在它之间呢?
    
    思考2,
    区间问题的实现,很多时候我们都要引入整段考虑的思想。
    这其实也就是CDQ分治的思想与具体实现。
    我们想要更新从(1,1)走到行[l,r]范围格点的方案数。
    而line[l,r]每个格子更新的来源,有两方面。
    1,line[1,l-1]
    2,line[l,r]内部
    我们假设在处理[l,r]时,所有line[1,l-1]对line[l,r]的贡献已经生效。
    于是现在只需要考虑来自于line[l,r]内部的贡献。
    并且定义过程solve(l,r),用于解决[l,r]内部贡献的转移。
    
    具体如何实现?
    回归到这道题——
    
    1,如果l==r,那不会生成任何贡献,直接结束。
    2,否则我们设m=(l+r)>>1;
    接下来只要依次实现以下3个步骤,这道题就做完了。
    (1),solve(l,m);
    (2),从[l,m]向[m+1,r]转移贡献;
    (3),solve(m+1,r);
    于是,关键落在要如何实现(2)上——
    此时,我们发现行关系已经是严格的小于关系,只需要使得列关系满足要求。如何使得?
    因为问题不是在数轴上,而是在矩形中。
    所以目前处理的区间是一个块,行数是[l,r],每行有m列。
    我们现在是想要把[l,mid]的状态传递到[mid+1,r]。
    发现只需要从左向右一列列枚举。
    状态结果的传递在(mid,r])实现,状态来源的累计在[l,mid]实现。
    就保证了行与列都是严格小于关系,这道题也就在O(nmlogn)的时间复杂度内解决了。
    
    */
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int mod = 1000000007;
    const int maxn = 800;
    int n,m,a[maxn][maxn],f[maxn][maxn],s[100000];
    void add(int &x,int y){
        x+=y;
        x%=mod;
    }
    void solve(int l,int r){
        if(l==r) return;
        int mid=(l+r)>>1;
        //第一步:处理[l,mid]
        solve(l,mid);
        //第二步:处理[l.mid]->(mid,r]的转移
        for(int i=l;i<=r;i++){
            for(int j=1;j<=m;j++){
                s[a[i][j]]=0;
            }
        }
        int all=0;
        //从左到右逐列枚举,以及拓扑逆序,保证了列的小于关系
        for(int j=1;j<=m;j++){
            //行累加只在[l,mid]展开,行转移在(mid,r]收集,保证了行的小于关系
            for(int i=mid+1;i<=r;i++) add(f[i][j],(all-s[a[i][j]]+mod)%mod);
            for(int i=l;i<=mid;i++){
                add(s[a[i][j]],f[i][j]);
                add(all,f[i][j]);
            }
        }
        //第三步:处理(mid,r]
        solve(mid+1,r);
    }
    int main(){
        while(~scanf("%d%d%*d",&n,&m)){
            for(int i=1;i<=n;i++){
                for(int j=1;j<=m;j++){
                    scanf("%d",&a[i][j]);
                }
            }
            memset(f,0,sizeof(f));
            f[1][1]=1;
            solve(1,n);
            printf("%d
    ",f[n][m]);
        }
        return 0;
    }
  • 相关阅读:
    JavaScript学习总结--事件冒泡与事件捕获
    JavaScript学习总结5--事件对象
    使用“微信公众号淘客管理工具”10分钟就能搭建淘客返利公众号
    C#实现通过拼多多分享微信公众号实现查询优惠券、佣金比率
    C# 实现生成带二维码的专属微信公众号推广海报
    公众号搭建淘宝、京东、拼多多查券平台
    开始使用Material UI
    Jenkins执行批处理文件失败
    取消Jquery mobile自动省略的文本
    C#调用Win32 api时的内存操作
  • 原文地址:https://www.cnblogs.com/imzscilovecode/p/7954633.html
Copyright © 2020-2023  润新知