奇袭

问题 C: 奇袭

时间限制: 1 Sec  内存限制: 256 MB
提交: 133  解决: 26
[提交][状态]

题目描述

由于各种原因,桐人现在被困在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

问题可以转化为一个长度为n的序列,求有多少个连续的子区间

定义ma[i]表示[i,mid]([mid+1,i])的最大值,mi[i]表示[i,mid]([mid+1,i])的最小值

分值考虑每一个区间的ans

对于[l,r]的一段区间,ans=两个子区间的ans之和加上跨mid的情况

1.最大值和最小值在同一侧,枚举每个点做边界,通过ma[i],mi[i]计算出另一个边界,如果符合情况就+1

2.最大值和最小值在异侧,加入最小值在左边i,最大值在右边j,j-i=ma[j]-mi[i]

得ma[j]-j=mi[i]-i;

两个指针z,z1从mid+1向r移动,while(ma[z]<ma[i]) t[ma[z]-z]-=1,z++;

while(mi[z1]>mi[i]) t[ma[z1]-z1]+=1,z1++;

根据ma[],mi[]单调可知,z右边的点肯定满足ma[j]>ma[i],z1左边的点满足mi[j]>mi[i],z到z1中间的点都可能是合法边界,ans+=t[mi[i]-i]

mi[i]-i可能为负数,再加上一个maxn,另一种情况类似

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define maxn 50000
#define INF 100000
#define LL long long 
using namespace std;
int n;
int a[maxn+5];
int ma[maxn+5],mi[maxn+5];
int t[maxn*2+5];
int max(int a,int b)
{
    return a>b ? a : b;
}
int min(int a,int b)
{
    return a<b ? a : b;
}
LL  get(int l,int r)
{
     if(l==r-1){
         if(abs(a[l]-a[r])==1) return 3;
         else return 2;
     }
     if(l==r){
         return 1;
     }
     LL ans=0;
     int mid=(l+r)/2;
     ans+=get(l,mid)+get(mid+1,r);
     ma[mid]=a[mid]; mi[mid]=a[mid];
     for(int i=mid-1;i>=l;i--){
         ma[i]=max(ma[i+1],a[i]);
         mi[i]=min(mi[i+1],a[i]);
     }
     ma[mid+1]=a[mid+1],mi[mid+1]=a[mid+1];
     for(int i=mid+2;i<=r;i++){
         ma[i]=max(ma[i-1],a[i]);
         mi[i]=min(mi[i-1],a[i]);
     }
     int op;
     for(int i=mid;i>=l;i--){
         op=ma[i]-mi[i]+i;
         if(op<=r&&op>mid&&ma[op]<=ma[i]&&mi[op]>=mi[i]) ans++; 
     }
     for(int i=mid+1;i<=r;i++){
         op=i-(ma[i]-mi[i]);
         if(op>=l&&op<=mid&&ma[op]<=ma[i]&&mi[op]>=mi[i]) ans++;
     }
     for(int i=l;i<=r;i++){ 
        t[ma[i]-i+maxn]=0; t[mi[i]-i+maxn]=0;
     }
     int z=mid+1,z1=mid+1;
     for(int i=mid;i>=l;i--){
         while(ma[z]<ma[i]&&z<=r){
            t[ma[z]-z+maxn]-=1; 
            z++;
         }
         while(mi[z1]>mi[i]&&z1<=r){
            t[ma[z1]-z1+maxn]+=1;
            z1++;
         }
         ans+=max(t[mi[i]-i+maxn],0);
         if(z==r+1) break;
     }
     for(int i=l;i<=r;i++){ 
       t[ma[i]+i]=0; t[mi[i]+i]=0;
     }
     z=mid,z1=mid;
     for(int i=mid+1;i<=r;i++){
         while(ma[z]<ma[i]&&z>=l){
            t[ma[z]+z]-=1;
            z--;
         }
         while(mi[z1]>mi[i]&&z1>=l){
            t[ma[z1]+z1]+=1;
            z1--;
         }
         ans+=max(t[mi[i]+i],0);
         if(z==l-1) break;
     }
     return ans;
}
int main()
{
     //freopen("in.txt","r",stdin);
     //freopen("raid1.in","r",stdin);
     int x,y;
     scanf("%d",&n);
     for(int i=1;i<=n;i++){
         scanf("%d%d",&x,&y);
         a[x]=y;
     }
     printf("%lld\n",get(1,n));
     //while(1);
     return 0;
}

posted @ 2017-09-09 09:01  HunterxHunterl  阅读(187)  评论(0编辑  收藏  举报