奇袭

时间限制: 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

 

题解

         考试的时候理解题意不够清楚,甚至都没注意到每行每列都只有一个,居然还一样骗了27的暴力分……但是如果看到的话应该不至于连64分的n^2暴力都想不到吧。数组显然是开不了二维的,直接用a[i]=j表示第i行j列有军队。这样就把矩阵上的问题转化到了序列里,看一个区间里面max(a[i])-min(a[i])=r-l是否满足,满足则ans++。但是这样统计很慢,需要分治。solve(l,r)求l到r区间里答案的个数,左区间的答案和右区间的答案是可以再向下分的,那么需要在这个函数里求的就是跨中线的答案了。

        定义:mal[i]、mil[i]:在[i,mid]区间内的最大、最小a;mar[i]、mir[i]:在[mid+1,i]区间内的最大、最小a。

        按区间最大最小值的位置分四种情况:

        max、min都在左边:从mid向l枚举i,如果区间[i,j]是一个合法解则应满足mal[i]-mil[i]=j-i,mar[j]<mal[i],mid<j<=r,mir[j]>mil[i]。找到合法解就把ans++。

        max、min都在右边同理。

        min在左边,max在右边:从mid向l枚举i,如果区间[i,j]是一个合法解则应满足mar[j]-mil[i]=j-i,移项得mar[j]-j=mil[i]-i,这样我们只需要统计从mid+1到r所有的mar[j]-j对应的可行解个数就可以通过mil[i]-i来得到i对应的解了,这可以用桶实现。mar当然是单调递增的,而mir是单调递减了,用两个指针f、t从mid+1向r移动,当mar[f]<mal[i]时右移f且tong[mar[f]-f]--,当mir[t]>mil[i]时右移t且tong[mar[t]-t]++,容易看出f经过的都是不可行的点而t经过的都是可行的点,对于每一个i移动两个指针完毕后ans+=tong[mil[i]-i]。

        max在左边,min在右边同理。

        很久没有做过这样的分治题,桶的应用也很新奇。对于一道题目来说,第一步的转化是最基础也是最关键的一步。每一次考试无论成绩如何,对自己的要求至少应该是“把应该想到的东西想清楚”,这大概不算高吧。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int sj=50005;
const int xf=50000;
int n,a[sj],mal[sj],mil[sj],mar[sj],mir[sj],tp,t[sj<<2],z,z1;
int bj(int x,int y)
{
    return x>y?x:y;
}
int xj(int x,int y)
{
    return x<y?x:y;
}
int solve(int l,int r)
{
    if(l==r) return 1;
    int mid=(l+r)>>1,ans=solve(l,mid)+solve(mid+1,r);
    mal[mid]=mil[mid]=a[mid];
    mar[mid+1]=mir[mid+1]=a[mid+1];
    for(int i=mid-1;i>=l;i--)
      mal[i]=bj(a[i],mal[i+1]),mil[i]=xj(a[i],mil[i+1]);
    for(int i=mid+2;i<=r;i++)
      mar[i]=bj(a[i],mar[i-1]),mir[i]=xj(a[i],mir[i-1]);
    for(int i=mid;i>=l;i--)
    {
      tp=mal[i]-mil[i]+i;
      if(tp>mid&&tp<=r&&mar[tp]<mal[i]&&mir[tp]>mil[i])
        ans++;
    }//both left
    for(int i=mid+1;i<=r;i++)
    {
      tp=i-(mar[i]-mir[i]);
      if(tp<=mid&&tp>=l&&mal[tp]<mar[i]&&mil[tp]>mir[i])
        ans++;
    }//both right
    z=z1=mid+1;
    for(int i=mid;i>=l;i--)
    {
       for(z;z<=r&&mal[i]>mar[z];z++)
         t[mar[z]-z+xf]--;
       for(z1;z1<=r&&mir[z1]>mil[i];z1++)
         t[mar[z1]-z1+xf]++;
       if(t[mil[i]-i+xf]>0)
         ans+=t[mil[i]-i+xf];
    }//min left   max right
    for(int i=mid+1;i<=r;i++)  t[mar[i]-i+xf]=0;
    z=z1=mid+1;
    for(int i=mid;i>=l;i--)
    {
       
       for(z;z<=r&&mal[i]>mar[z];z++)
         t[mir[z]+z+xf]++;
       for(z1;z1<=r&&mir[z1]>mil[i];z1++)
         t[mir[z1]+z1+xf]--;
       if(t[mal[i]+i+xf]>0)
         ans+=t[mal[i]+i+xf];       
    }//max left   min right
    for(int i=mid+1;i<=r;i++) t[mir[i]+i+xf]=0;
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1,j;i<=n;i++)
    {
       scanf("%d",&j);
       scanf("%d",&a[j]);
    }
    printf("%d",solve(1,n));
    return 0;
}
raid

 

posted @ 2017-09-10 11:02  moyiii  阅读(266)  评论(1编辑  收藏  举报