码迷,mamicode.com
首页 > 其他好文 > 详细

7.16T3奇袭

时间:2019-07-17 18:54:24      阅读:96      评论:0      收藏:0      [点我收藏+]

标签:iostream   style   one   ace   递归   code   gif   play   输出   

奇袭

题目描述

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

这是道毒瘤题,毒瘤的不能再毒瘤,你可以想出各种O(n5),O(n4),O(n3)以至于O(n2)的算法,然而你都会T,且卡常卡不过去,而正解是个O(nlogn)的

关于超时的算法我其实还是想说一说,由于考试的时候我zz,所以最好也就打了一个n4,实际上这题的n3很好打,就是枚举起始点以及块的大小,这个复杂度最多也就T27吧

然后是n2的,那么你就需要认真读题,我没好好读题,我不是人,注意一下我标红加粗那一行(为什么考试的时候没人给我加粗。。。。。),他说每行每列就一个,那我们把这个方格图拍扁怎么样,我们把它从二维搞成一维,也就是表示成a[x]=y的形式,那么思考一下,对于一个区间[L,R],这个LR就是横坐标的范围,也就是子方格图的宽,而a[L]到a[R]之间的max和min就是纵坐标的范围,也就是子方格图的长,那只要max-min=R-L,这个区间就是一个合法的区间,这样的话两层循环枚举L和R就达到了n2,就可以愉快的T67了,据某些Dalao说n2卡一卡可以T91

接下来就是正解了,需要用到刚才拍扁方格的思路,还要用的二分,单调性以及桶

如果我们用二分的想法的话,ans[L,R]=ans[L,mid]+ans[mid+1,R]+跨过mid部分的合法方案,关于前两项,递归下去去算就没问题,重点需要解决的是第三项,我们来想一想这个跨过mid都包含些什么情况

1.min max都在左边

2.min max都在右边

3.min在左边max在右边

4.min在右边max在左边

我们化减一下这四种方案变成  一.最值在同侧  二.最值在异侧

首先我们需要记录一下从mid到L中间这些区间内的最值以及mid+1到R中间这些区间的最值

一.最值在同侧(以均在左侧为例)

如果最值都在左侧,那么就需要满足max[l]-min[l]=r-l,这个和n2的思路是一样的,我们移一下项,就得到了r=max[l]-min[l]+l,那我们就可以枚举这个l,来确定每一个对应的r,来看是不是符合条件

如果都在右侧,同样的思路枚举右端点就可以了

二.最值在异侧(以最小值在左最大值在右为例)

我在说正解之前提到了单调性,那什么具有单调性呢?这个区间max min具有单调性,因为左区间的最值都是有mid开始推的,那从mid到L,min严格不上升,max严格不下降,这个很显然嘛,那同理,从mid+1到Rmin严格不上升,max严格不下降,我们假设左区间是[L,mid],那什么样的右区间是合法的呢?我们设两个指针r1,r2,把他们的初始位置放在mid+1处,我们在移动指针之前先想一想右区间应该满足什么才可以称作合法,诶呀,不就是min[r]>min[l] max[r]>max[l]嘛,关于这个max我们可以找到第一个比max[l]大的,由于这个单调性,第一个max及其之后都大于max[l],我们还可以找到最后一个比min[l]大的,同样由于这个单调性最后一个之后的min都小于min[l],变成了不合法区间,这样的话我们用r1,r2这两个指针找到这个第一个和最后一个,这中间就是合法方案,他还需要满足一个什么条件来着?max[r]-min[l]=r-l,还是移项,那就可以变形为max[r]-r=min[l]-l,这样的话等式两边就变成了两个独立的部分,我们把这个独立的部分扔进桶里,让他进行加加减减的操作我们就可以得到正确的区间数,当然了,我们最开始定下了大左区间[L,mid],我们现在需要缩小它,那r1和r2两个指针也就接着移动就可以了

如果最大值在左,最小值在右,也是一样的思路,在左边找到合法区间,移动右区间就可以了

当然这题还可以翻转,就是那个reverse,代码会变短,复杂度的话多个logn

注意:桶记得清空,min[l]-l可能小于0,所以再加个n

技术图片
 1 #include<iostream>
 2 #include<cstdio>
 3 #define maxn 50010
 4 #define ll long long
 5 using namespace std;
 6 int n,maxx,minn;
 7 ll ans;
 8 int a[maxn],mi[maxn],ma[maxn],tong[maxn*3];
 9 inline int read()
10 {
11     int e=0,f=1;  char ch=getchar();
12     while(ch<0||ch>9)
13     {
14         if(ch==-)  f=-1;
15         ch=getchar();
16     }
17     while(ch>=0&&ch<=9)  {e=(e<<3)+(e<<1)+(ch-48);  ch=getchar();}
18     return e*f;
19 }
20 ll fz(int l,int r)
21 {
22     if(l==r)  return 1;
23     ll da=0,sum=0;  int mid=(l+r)>>1;
24     da=fz(l,mid)+fz(mid+1,r);
25     ma[mid]=mi[mid]=a[mid];
26     for(int i=mid-1;i>=l;--i)  {ma[i]=max(ma[i+1],a[i]);  mi[i]=min(mi[i+1],a[i]);}
27     ma[mid+1]=mi[mid+1]=a[mid+1];
28     for(int i=mid+2;i<=r;++i)  {ma[i]=max(ma[i-1],a[i]);  mi[i]=min(mi[i-1],a[i]);}
29     for(int i=mid;i>=l;--i)//都在左侧
30     {
31         int R=ma[i]-mi[i]+i;
32         if(R<=mid||R>r)  continue;
33         if(ma[R]<ma[i]&&mi[R]>mi[i])  sum++;
34     }
35     for(int i=mid+1;i<=r;++i)//都在右侧
36     {
37         int L=i-ma[i]+mi[i];
38         if(L>=mid+1||L<l)  continue;
39         if(ma[L]<ma[i]&&mi[L]>mi[i])  sum++;
40     }
41     int r1=mid+1,r2=mid+1;//最小值在左,最大值在右
42     while(r1<=r&&mi[r1]>mi[l])  {tong[n+ma[r1]-r1]++;  r1++;}
43     while(r2<=r&&ma[r2]<ma[l])  {tong[n+ma[r2]-r2]--;  r2++;}
44     for(int L=l;L<=mid;++L)
45     {
46         while(r1>mid+1&&mi[r1-1]<mi[L])  {r1--;  tong[n+ma[r1]-r1]--;}
47         while(r2>mid+1&&ma[r2-1]>ma[L])  {r2--;  tong[n+ma[r2]-r2]++;}        
48         if(tong[n+mi[L]-L]>0)  sum+=tong[n+mi[L]-L];
49     }
50     for(int i=mid+1;i<=r;++i)  tong[n+ma[i]-i]=0;
51     r1=mid;  r2=mid;//最大值在坐,最小值在右
52     while(r1>=l&&mi[r1]>mi[r])  {tong[n+ma[r1]+r1]++;  r1--;}
53     while(r2>=l&&ma[r2]<ma[r])  {tong[n+ma[r2]+r2]--;  r2--;}
54     for(int R=r;R>=mid+1;--R)
55     {
56         while(r1<mid&&mi[r1+1]<mi[R])  {r1++;  tong[n+ma[r1]+r1]--;}
57         while(r2<mid&&ma[r2+1]>ma[R])  {r2++;  tong[n+ma[r2]+r2]++;}
58         if(tong[n+mi[R]+R]>0)  sum+=tong[n+mi[R]+R];
59     }
60     for(int i=l;i<=mid;++i)  tong[n+ma[i]+i]=0;
61     da+=sum;
62     //cout<<"from"<<l<<"to"<<r<<"sum="<<sum<<endl;
63     return da;
64 }
65 int main()
66 {
67     n=read();
68     for(int i=1;i<=n;++i)  {int o=read(),p=read();  a[o]=p;}
69     ans=fz(1,n);
70     printf("%lld\n",ans);
71     return 0;
72 }
View Code

 

7.16T3奇袭

标签:iostream   style   one   ace   递归   code   gif   play   输出   

原文地址:https://www.cnblogs.com/hzjuruo/p/11202031.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!