[apio2017]斑斕之地
將不是河流的格仔染成白色,是河流的格仔染成黑色,那麼連通塊數就是白色格仔數$-1*2$的聯通白色格仔數$-2*1$的聯通白色格仔數$+2*2$的聯通白色格仔數。
我們考慮每個格仔與它左邊、上邊、左上三個格仔的連通性(同為白色視為聯通)。
為了方便起見,對於每個$2*2$的格仔,我們將它編號,從左往右、從上往下依次編號為$1,2,3,4$。
我們將$1,2,3$與$4$的連通性都歸為$4$號格仔對答案的貢獻。
顯然聯通情況有$5$種:$1,2,3,4$、$2,3,4$、$2,4$、$3,4$、$4$。
對於第一種情況,$4$號點對答案的貢獻為$1-1-1+1=0$
對於第二種情況,$4$號點對答案的貢獻為$1-1-1=-1$
對於第三、四種情況,$4$號點對答案的貢獻為$1-1=0$
對於第五種情況,$4$號點對答案的貢獻為$1$
可以發現第
一、三、四種情況對答案沒有影響,而第
二、五種情況只會出現在乙個連通塊的最左邊和最上邊兩排(有一種特殊情況後邊再說明)。
對於最左邊,如果有乙個格仔是第二種情況,那麼在這個點的同一行的最左邊那個點就會是第五種情況,這兩個格仔的貢獻抵消。
對於最上邊,如果有乙個格仔是第二種情況,那麼在這個點的同一列的最上邊那個點就會是第五種情況,這兩個格仔的貢獻抵消。
但可以發現最左上的那個格仔是第五種情況卻沒有其他格仔與它的貢獻抵消,所以只有這個格仔對這個連通塊有貢獻。
這樣有乙個特例就是河流被這個連通塊包圍起來,即這個連通塊是中空的。
那麼右邊和下邊也會出現第
二、五種情況,而對於右下兩部分格仔中的左上那個格仔是第二種情況,會將上面那個對連通塊有貢獻的格仔抵消掉(如下圖所示),所以對於這種情況特判一下將答案加一即可。
剩下的就是如何統計上述的四種連通塊的個數。
因為地圖總大小是$4*10^$,無法對每個點存是否有上述四種貢獻。
但可以發現河流的格仔最多只有$2*10^5$個格仔,我們分別記錄哪些格仔沒有上述四種貢獻,然後用總貢獻減一下即可。
對於整個地圖將橫座標作為版本,對縱座標建線段樹即建立四棵可持久化線段樹分別維護上述四種資訊。
注意彩虹蛇可能走之前走過的格仔,要判重避免重複統計。
#include#include#include#include#include#include#include#include#include#include#include#define ll long longusing namespace std;
int n,m,q,k;
mapmp[200010];
int px[200010];
int py[200010];
int tot;
int mx1,mx2,mn1,mn2;
int fx,fy;
int size;
vectort1[200010];
vectort2[200010];
vectort3[200010];
vectort4[200010];
char ch[200010];
int a,b,c,d;
ll ans;
struct lty
int mid=(l+r)>>1;
if(k<=mid)
else
}int query(int x,int y,int l,int r,int l,int r)
if(l<=l&&r<=r)
int mid=(l+r)>>1;
int res=0;
if(l<=mid)
if(r>mid)
return res;
}}tr1,tr2,tr3,tr4;
int main()
for(int i=1;i<=k;i++)
else if(ch[i]=='s')
else if(ch[i]=='e')
else
px[tot]=fx,py[tot]=fy;
mx1=max(mx1,fx);
mn1=min(mn1,fx);
mx2=max(mx2,fy);
mn2=min(mn2,fy);
mp[fx][fy]=1;
} for(int i=1;i<=tot;i++)
mp[x][y]=2;
t1[x].push_back(y);
if(y>1)t2[x].push_back(y);
if(y1)t3[x].push_back(y);
if(x1&&y>1)t4[x].push_back(y);
if(x1&&!mp[x][y-1]&&!mp[x+1][y])t4[x+1].push_back(y);
if(x>1&&ymx1&&bmx2)
ans+=1ll*(d-b)*(c-a)-tr1.query(tr1.root[a-1],tr1.root[c],1,m,b,d);
ans-=1ll*(d-b-1)*(c-a)-tr2.query(tr2.root[a-1],tr2.root[c],1,m,b+1,d);
ans-=1ll*(d-b)*(c-a-1)-tr3.query(tr3.root[a],tr3.root[c],1,m,b,d);
ans+=1ll*(d-b-1)*(c-a-1)-tr4.query(tr4.root[a],tr4.root[c],1,m,b+1,d);
printf("%lld\n",ans);
} return 0;
}
可持久化線段樹總結(可持久化線段樹,線段樹)
最近正在學習一種資料結構 可持久化線段樹。看了網上的許多部落格,弄了幾道模板題,思路有點亂了,所以還是來總結整理下吧。你需要維護這樣的乙個長度為 n 的陣列,支援如下幾種操作 在某個歷史版本上修改某乙個位置上的值 訪問某個歷史版本上的某一位置的值 此外,每進行一次操作 對於操作2,即為生成乙個完全一...
可持久化線段樹
可持久化線段樹,意思是可以查詢歷史記錄的線段樹。又叫主席樹。我們可以通過記錄不同的根節點,並在每乙個更新到的節點處新建必要的節點。詢問不同版本的主席樹,只需要進入不同的根節點即可。例題 給定n,m,輸入n個數組成的數列,有m個詢問,每次詢問l,r這個區間中,第k小的數的值。分析 這個題可以巧妙運用主...
可持久化線段樹
以p3919 模板 可持久化陣列 可持久化線段樹 平衡樹 為例。知識點 1.練習可持久化線段樹 2.線段樹維護數列。線段樹維護數列單點查詢僅需o logn 3.記得return root 4.記得設定左右兒子 5.有時需注意cnt的初始大小 include using namespace std i...