1. 程式人生 > >非確定性有窮狀態決策自動機練習題Vol.2 C. 奇襲

非確定性有窮狀態決策自動機練習題Vol.2 C. 奇襲

# 非確定性有窮狀態決策自動機練習題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