【NOIP2016提高A組8.12】奇襲

Description

由於各種原因,桐人現在被困在Under World(以下簡稱UW)中,而UW馬上要迎來最終的壓力測試——魔界入侵。
唯一一個神一般存在的Administrator被消滅了,靠原本的整合騎士的力量是遠遠不夠的。所以愛麗絲動員了UW全體人民,與整合騎士一起抗擊魔族。
在UW的駐地可以隱約看見魔族軍隊的大本營。整合騎士們打算在魔族入侵前發動一次奇襲,襲擊魔族大本營!
爲了降低風險,愛麗絲找到了你,一名優秀斥候,希望你能在奇襲前對魔族大本營進行偵查,並計算出襲擊的難度。
經過偵查,你繪製出了魔族大本營的地圖,然後發現,魔族大本營是一個N×N的網格圖,一共有N支軍隊駐紮在一些網格中(不會有兩隻軍隊駐紮在一起)。
在大本營中,每有一個k×k(1≤k≤N)的子網格圖包含恰好k支軍隊,我們襲擊的難度就會增加1點。
現在請你根據繪製出的地圖,告訴愛麗絲這次的襲擊行動難度有多大。

Input

第一行,一個正整數N,表示網格圖的大小以及軍隊數量。
接下來N行,每行兩個整數,Xi,Yi,表示第i支軍隊的座標。
保證每一行和每一列都恰有一隻軍隊(即每一個Xi和每一個Yi都是不一樣的)。

Output

一行,一個整數表示襲擊的難度。

Sample Input

5
1 1
3 2
2 4
5 5
4 3

Sample Output

10
【樣例解釋】
顯然,分別以(2,2)和(4,4)爲左上,右下頂點的一個子網格圖中有3支軍隊,這爲我們的難度貢獻了1點。
類似的子網格圖在原圖中能找出10個。
##Data Constraint
對於30%的數據,N ≤ 100
對於60%的數據,N ≤ 5000
對於100%的數據,N ≤ 50000

Solution

乍一看以爲是一道數論題
但是看一個條件
保證每一行和每一列都恰有一隻軍隊(即每一個Xi和每一個Yi都是不一樣的)
這說明我們可以把圖壓扁,變成一維(被歌者降維了)
意思是原來是a[x,y]=1a[x,y]=1變成a[x]=ya[x]=y
之後變成數列上的問題。
怎樣判斷一個狀態是否合法呢?

給定一個l和r,l和r代表的就是列,而a[l]和a[r]就是行,**如果max(l,r)min(l,r)=lrmax(l,r)-min(l,r)=l-r(max(l,r)表示l~r之間的a的最大值,min是最小值)**那就ans++

直接暴力枚舉就有60分了
100分怎麼做?
有兩種做法,線段樹和分治,這裏考慮分治。
每次通過特殊的方法找max和min來判斷是否要ans++
對於當前的一段l~r,還有它的中點m,已中點分開,如果找到的兩個左右邊界在同側,那就分治下去,只考慮左右邊界跨越了中點
分爲四種情況考慮
1、max和min同在m左側
2、max和min同在m右側
3、min在左,max在右
4、min在右,max在左
爲了解決問題,定義ma[i]ma[i]表示從中點到i之間的最大值,顯然從中間往兩邊分別單調遞增
同樣定義mi[i]mi[i]爲最小值,顯然從中點往兩邊分別單調遞減
mi和ma直接在每次分治時暴力處理出
左右邊界分別設爲i和j
先從m向l枚舉i

第一種情況

對於i算出對應的j
j=ma[i]mi[i]+ij=ma[i]-mi[i]+i
然後判斷j必須在右側,且沒有出界,並且ma[j]<mi[i]ma[j]<mi[i]mi[j]>ma[i]mi[j]>ma[i],因爲max與min都在左側,那右邊就不能有比左邊最大大的和比左邊最小小的。

第二種

與第一種對稱就行了

第三種

設兩個指針z1z1z2z2,作用等會說
與第一種一樣,因爲左小右大,所以必須保證ma[j]>ma[i]ma[j]>ma[i]mi[j]>mi[i]mi[j]>mi[i]即左邊不能比右邊大,左邊必須比右邊小
那麼就用z1維護在保證ma[z1]>ma[i]ma[z1]>ma[i]時可取得最小值,因爲ma[i]ma[i]是單調的,所以z1的取值也是隨着i的減小而增大的
z2則維護保證mi[z2]>mi[i]mi[z2]>mi[i]的最大值,同理,z2也是隨着i的減小而增大的
那怎麼計算數量呢,看張圖
這裏寫圖片描述
z1右邊的所有點滿足ma[z1]>ma[i]ma[z1]>ma[i],z2左邊的所有點滿足mi[z2]>mi[i]mi[z2]>mi[i]
那z1和z2之間的就是j的取值範圍了
具體實現:
在z2往右走的時候在桶裏+1,z1走的時候桶裏-1最後就可以統計出數量了
桶的哪個位置改變呢?
要保證ma(j)mi(i)=jima(j)-mi(i)=j-i
移項得ma(j)j=mi(i)ima(j)-j=mi(i)-i
而z1、z2就是爲了找出j的範圍,所以在桶裏就是ma[z1]-z1或z2所在的位置相加減
ans+=mi[i]ians+=mi[i]-i就行了
p.s:mi[i]imi[i]-i可能是負數,c++就加一個很打的數就行了

第四種

與第三種對稱

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 51000
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int a[N],n,ans=0,mi[N],ma[N],t[N*5];
void dg(int l,int r)
{
	if(l==r) return;
	int m=(l+r)/2;
	mi[m]=ma[m]=a[m];
	mi[m+1]=ma[m+1]=a[m+1];
	fd(i,m-1,l) mi[i]=min(a[i],mi[i+1]),ma[i]=max(a[i],ma[i+1]);
	fo(i,m+2,r) mi[i]=min(a[i],mi[i-1]),ma[i]=max(a[i],ma[i-1]);
	int z1=m+1,z2=m+1;
	fd(i,m,l) 
	{
		int j=ma[i]-mi[i]+i;
		if(j>m&&j<=r&&ma[j]<ma[i]&&mi[j]>mi[i]) ans++;
		while(z2<=r&&mi[z2]>=mi[i]) t[ma[z2]-z2+N]++,z2++;
		while(z1<=r&&ma[z1]<=ma[i]) t[ma[z1]-z1+N]--,z1++;
		ans+=max(t[mi[i]-i+N],0);
	}
	fo(i,l,r) t[ma[i]-i+N]=0;
	z1=m;z2=m;
	fo(i,m+1,r)
	{
		int j=i-ma[i]+mi[i];
		if(j<=m&&j>=l&&ma[j]<ma[i]&&mi[j]>mi[i]) ans++;
		while(z2>=l&&mi[z2]>=mi[i]) t[ma[z2]+z2+N]++,z2--;
		while(z1>=l&&ma[z1]<=ma[i]) t[ma[z1]+z1+N]--,z1--;
		ans+=max(t[mi[i]+i+N],0);
	}
	fo(i,l,r) t[ma[i]+i+N]=0;
	dg(l,m);dg(m+1,r);
}
int main()
{
	scanf("%d",&n);
	fo(i,1,n)
	{
		int x,y;scanf("%d%d",&x,&y);
		a[x]=y;
	}
	dg(1,n);
	printf("%d",ans+n);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章