zoukankan      html  css  js  c++  java
  • 单调栈+桶+分治 奇袭

    问题 C: 奇袭
    时间限制: 1 Sec 内存限制: 256 MB
    题目描述
    由于各种原因,桐人现在被困在Under World(以下简称UW)中,而UW马上 要迎来最终的压力测试——魔界入侵。
    唯一一个神一般存在的Administrator被消灭了,靠原本的整合骑士的力量 是远远不够的。所以爱丽丝动员了UW全体人民,与整合骑士一起抗击魔族。
    在UW的驻地可以隐约看见魔族军队的大本营。整合骑士们打算在魔族入侵前 发动一次奇袭,袭击魔族大本营!
    为了降低风险,爱丽丝找到了你,一名优秀斥候,希望你能在奇袭前对魔族 大本营进行侦查,并计算出袭击的难度。
    经过侦查,你绘制出了魔族大本营的地图,然后发现,魔族大本营是一个N ×N的网格图,一共有N支军队驻扎在一些网格中(不会有两只军队驻扎在一起)。
    在大本营中,每有一个k×k(1≤k≤N)的子网格图包含恰好k支军队,我们袭 击的难度就会增加1点。
    现在请你根据绘制出的地图,告诉爱丽丝这次的袭击行动难度有多大。
    输入
    第一行,一个正整数N,表示网格图的大小以及军队数量。
    接下来N行,每行两个整数,Xi,Yi,表示第i支军队的坐标。
    保证每一行和每一列都恰有一只军队,即每一个Xi和每一个Yi都是不一样 的。
    输出
    一行,一个整数表示袭击的难度。
    样例输入
    5
    1 1
    3 2
    2 4
    5 5
    4 3
    样例输出
    10
    提示
    【样例解释】
    显然,分别以(2,2)和(4,4)为左上,右下顶点的一个子网格图中有3支军队,
    这为我们的难度贡献了1点。
    类似的子网格图在原图中能找出10个。
    【数据范围】
    对于30%的数据,N ≤ 100
    对于60%的数据,N ≤ 5000
    对于100%的数据,N ≤ 50000

    N^2效率很好想的,但如何搞出N*logN的效率。
    因为方格每行每列都只有一个,所以把它搞成一个序列,求有多少个区间的max-min的值等于区间长度就行了。
    如果枚举每个区间(说白了就是N^2)过不去的,但如果少枚举区间,而将其所包含区间不重复地筛选,那么可以用分治。
    对于一个区间[l,r],[l,mid],[mid+1,r]继续分治,然后处理跨过mid的区间。而对于跨过mid的区间有两大种情况。
    1,max,min均在区间的左侧(均在右侧与之对称,处理方法相同)
    2,min在左侧,max在右侧(同理)

    考虑如何处理。
    首先预处理出单调栈,从mid分别向左向右,处理出到此处的最大最小值。
    情况1,枚举左端点,此处的max-min即为当前区间的长度(设定max,min在同一侧)就可以计算出右端点的位置,判断右端点是否在mid右侧(防止加重)并判断此点的max,min是否合适。
    情况2,在这里就用到桶了。因为当前区间满足rmax[right])-lmin[lift]=right-lift;
    移项得rmax[right]-right=lmin[lift]-lift;只要找两边满足这个等式的点就行了。用桶扫完左边,右边去找,找到右侧第一个min<=min[l]的位置(因为是单调的,所以<=最小的不可能合适),同理第一个max>max[l]的位置才合适。再找这两个端点的过程中把不合适的踢出桶,合适的重新加进来。因为满足单调,所以桶中满足的区间也是单调的,线性找就行了。
    O(N*logN^2)为什么多了个log呢,关于对称的情况,我单纯的把区间反转,调了个STL,省代码,费时间。

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int n,a[500005],tong[600010];
    int lmin[500005],rmin[500005],lmax[500005],rmax[500005];
    int *hh=tong+300005;//为防止越界,用了一个指针
    int get(int l,int r,int m)
    {
        int s=0;
        lmax[m]=lmin[m]=a[m];
        rmax[m+1]=rmin[m+1]=a[m+1];
        for(int i=m-1;i>=l;i--)
        {
            lmax[i]=lmax[i+1]>a[i]?lmax[i+1]:a[i];
            lmin[i]=lmin[i+1]<a[i]?lmin[i+1]:a[i];
        }
        for(int i=m+2;i<=r;i++)
        {
            rmax[i]=rmax[i-1]>a[i]?rmax[i-1]:a[i];
            rmin[i]=rmin[i-1]<a[i]?rmin[i-1]:a[i];
        }
        for(int i=l;i<=m;i++)
        {
            int j=i+lmax[i]-lmin[i];
            if(j>m&&rmax[j]<lmax[i]&&rmin[j]>lmin[i])s++;
        }
        int p=m+1,q=m+1;hh[rmax[q]-q]++;
        while(q<r&&rmin[q+1]>lmin[l])q++,hh[rmax[q]-q]++;
        while(p<=r&&rmax[p]<lmax[l])hh[rmax[p]-p]--,p++;
        for(int i=l;i<=m;i++)
        {
            while(p>m+1&&rmax[p-1]>lmax[i])p--,hh[rmax[p]-p]++;
            while(q>m&&rmin[q]<lmin[i])hh[rmax[q]-q]--,q--;
            if(hh[lmin[i]-i]>0)s+=hh[lmin[i]-i];
        }
        for(int i=m+1;i<=r;i++)hh[rmax[i]-i]=0;
        return s;
    }
    inline int work(int l,int r)
    {
        if(l==r)return 1;
        int mid=(l+r)/2;
        int s=work(l,mid)+work(mid+1,r);
        s+=get(l,r,mid);
        reverse(a+l,a+r+1);
        if((r-l+1)%2)mid--;
        s+=get(l,r,mid);
        reverse(a+l,a+r+1);
        return s;
    } 
    int main()
    {
        cin>>n;int x,y;
        for(int i=1;i<=n;i++)scanf("%d%d",&x,&y),a[x]=y;
        cout<<work(1,n);
    }
  • 相关阅读:
    测试平台系列(69) 数据构造器支持sql语句
    Selenium获取动态图片验证码
    测试平台系列(68) 解决数据驱动带来的麻烦
    测试平台系列(67) 玩转数据驱动
    3班6组项目测试心得
    3班6组第一次迭代博客
    需求分析心得
    数据库设计心得
    结对编程总结
    代码欣赏
  • 原文地址:https://www.cnblogs.com/QTY2001/p/7632683.html
Copyright © 2011-2022 走看看