非確定性有窮狀態決策自動機練習題Vol.2 C. 奇襲
阿新 • • 發佈:2020-08-18
# 非確定性有窮狀態決策自動機練習題Vol.2 C. 奇襲
## 題目描述
由於各種原因,桐人現在被困在$Under World$(以下簡稱$UW$)中,而$UW$馬上 要迎來最終的壓力測試——魔界入侵。
唯一一個神一般存在的$Administrator$被消滅了,靠原本的整合騎士的力量 是遠遠不夠的。所以愛麗絲動員了$UW$全體人民,與整合騎士一起抗擊魔族。
在$UW$的駐地可以隱約看見魔族軍隊的大本營。整合騎士們打算在魔族入侵前 發動一次奇襲,襲擊魔族大本營! 為了降低風險,愛麗絲找到了你,一名優秀斥候,希望你能在奇襲前對魔族大本營進行偵查,並計算出襲擊的難度。
經過偵查,你繪製出了魔族大本營的地圖,然後發現,魔族大本營是一個$N \times 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$
## 分析
用線段樹即可解決
首先,我們發現,如果要滿足 $k×k(1≤k≤N)$ 的子網格圖包含恰好 $k$ 支軍隊
那麼這 $k$ 只軍隊的最大橫座標減去最小橫座標恰好等於這 $k$ 只軍隊的最大縱座標減最小縱座標
兩維不好處理,因此我們把橫座標作為下標,縱座標作為權值
這樣原問題就變成了在一個排列中有多少區間內的數是連續的
我們發現這可以用線段樹去維護
我們把線段樹的節點定義為以某個點為左端點,以掃到的點為右端點的區間中連續區間的個數
線段樹要維護的資訊有連續區間個數的最小值,該最小值的個數,以及區間加和操作中的 $lazy$ 標記
每次從右邊新加入一個點 $i$ 時,我們把區間 $[1,i]$ 整體加 $1$
代表此時又多了一個不連續的區間
此時我們去找 $a[i]+1$ 和 $a[i]-1$ 的位置,如果它們的位置在 $i$ 的左邊,我們就把 $[1,a[i]-1]$ 或者 $[1,a[i]+1]$ 整體減一,代表包含 $a[i]+1$ 或者 $a[i]-1$ 的區間可以與 $a[i]$ 合併形成一個大區間
每次列舉一個右端點時就單獨計算一下價值
## 程式碼
``` cpp
#include
#include
#include
const int maxn=1e6+5;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
int a[maxn],n,wz[maxn];
struct asd{
int l,r,min,cnt,laz;
}tr[maxn<<1];
void push_up(int da){
if(tr[da<<1].min==tr[da<<1|1].min){
tr[da].min=tr[da<<1].min;
tr[da].cnt=tr[da<<1].cnt+tr[da<<1|1].cnt;
} else if(tr[da<<1].min>1;
build(da<<1,l,mids);
build(da<<1|1,mids+1,r);
push_up(da);
}
int cx(int da,int l,int r){
if(tr[da].l>=l && tr[da].r<=r){
if(tr[da].min==1) return tr[da].cnt;
return 0;
}
push_down(da);
int mids=(tr[da].l+tr[da].r)>>1;
int ans=0;
if(l<=mids) ans+=cx(da<<1,l,r);
if(r>mids) ans+=cx(da<<1|1,l,r);
return ans;
}
void xg(int da,int l,int r,int val){
if(tr[da].l>=l && tr[da].r<=r){
tr[da].min+=val;
tr[da].laz+=val;
return;
}
push_down(da);
int mids=(tr[da].l+tr[da].r)>>1;
if(l<=mids) xg(da<<1,l,r,val);
if(r>mids) xg(da<<1|1,l,r,val);
push_up(da);
}
int main(){
n=read();
for(int i=1;i<=n;i++){
int aa,bb;
aa=read(),bb=read();
a[aa]=bb;
wz[a[aa]]=aa;
}
build(1,1,n);
long long ans=0;
for(int i=1;i<=n;i++){
if(i>1) xg(1,1,i-1,1);
if(wz[a[i]-1]<=i && wz[a[i]-1]){
xg(1,1,wz[a[i]-1],-1);
}
if(wz[a[i]+1]<=i && wz[a[i]+1]){
xg(1,1,wz[a[i]+1],-1);
}
ans+=(long long)cx(1,1,i);
}
printf("%lld\n",ans);
return