.(Single Dot)
傳送門
題意:給若干橫着和豎着的線段(柵欄),求位於(0,0)的牛可以往外走的面積。
思路:
我是真的不會(。
看了大佬給的題解,原來是把圖片壓縮,用二維網格表示,每條線都緊挨着,然後暴力BFS。(思路就是這麼簡單)
重點在於如何實現把圖片壓縮。
爲了方便理解,畫了三個圖:
圖一是原圖的亞子。線段有正有負,牛在原點處。
然後,我們根據原圖的線段的相對位置,建立一個新圖。此時,每兩條線段之間,如果是空的,即變爲緊挨着。粉色區域爲無限區域,如(0,0)代表的是(-inf,-inf)。粉色區域以外爲越界,不可達。
如果牛處於封閉的地段,那麼BFS將永遠不會到達粉色區域。而一旦到達了粉色區域,就可以繞着粉色區域轉圈,因此,只要能達到無限,那麼(0,0)是一定會被vis過的,不論是哪裏沒有封閉住。
線段們如何被表示呢?首先,我們得把圖片變成二維網格(通過sort和unique和數組記錄與查找)(紫色數字)。這樣一來,每一條線段都夾縫在兩排格子之間。我們用格子標記線段:如:如果有一條線段連接[0][2]和[2][2],那麼在格子[0][1]、[1][1]與[0][2]、[1][2]之間便有一條線段,那麼,我們設up[0][1]=1,u[1][1]=1和down[0][2]=1、down[1][2]=1,表示他們之間的這條線。
問題又來了:如果牛的位置位於線段們的左邊(如圖),那麼(實操)找lower_bound的時候,會把起點放在藍色區域。而這時如果正好藍色區域是封閉的,那麼答案糾錯了。(正確應該是能遍歷到粉色區域爲inf)
怎麼解決這個問題呢?很簡單,別這樣投射,把起點投射到藍色區域左下角的區域就行了。同樣也是從牛出發,不過意味着從牛的左下角方塊開始出發而已。
這個問題在牛位於右邊的時候不會出現,因爲是lower_bound,牛的投射點會剛好在粉色區域之中。
代碼:
#include<bits/stdc++.h>
using namespace std;
const int maxn=3006;//最多要開3000,因爲n那裏輸入兩個,m那裏輸入1個,共3個
const int inf=1e9+7;
typedef long long ll;
int dir[][2]={1,0,-1,0,0,1,0,-1};
int a[maxn],b[maxn],c[maxn],D[maxn],e[maxn],f[maxn];//用於查詢
int u[maxn][maxn],d[maxn][maxn],l[maxn][maxn],r[maxn][maxn],vis[maxn][maxn];//記錄線段(方格邊界)
vector<int>xx({-inf,inf}),yy({-inf,inf});//先塞入粉色區域
void bfs(int x,int y){
vis[x][y]=1;
queue<pair<int,int>>q;
q.push({x,y});
while(!q.empty()){
x=q.front().first,y=q.front().second;
q.pop();
for(int i=0;i<4;i++){
if(i==0&&r[x][y]) continue;
if(i==1&&l[x][y]) continue;
if(i==2&&u[x][y]) continue;
if(i==3&&d[x][y]) continue;
int nx=x+dir[i][0];
int ny=y+dir[i][1];
if(nx<0||ny<0) continue;//越界
if(nx>=xx.size()-1||ny>=yy.size()-1) continue;//越界
if(vis[nx][ny]) continue;
vis[nx][ny]=1;
q.push({nx,ny});
}
}
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
scanf("%d",&b[i]);
scanf("%d",&c[i]);
xx.push_back(a[i]);
xx.push_back(b[i]);
yy.push_back(c[i]);
}
for(int i=0;i<m;i++){
scanf("%d",&D[i]);
scanf("%d",&e[i]);
scanf("%d",&f[i]);
xx.push_back(D[i]);
yy.push_back(e[i]);
yy.push_back(f[i]);
}
sort(xx.begin(),xx.end());//先按照從小到大sort
sort(yy.begin(),yy.end());
xx.resize(unique(xx.begin(),xx.end())-xx.begin());//然後把重複項去掉!(壓縮)
yy.resize(unique(yy.begin(),yy.end())-yy.begin());
for(int i=0;i<n;i++){
//接下來對於每條線段,先把線段查找出來,再用新的壓縮座標標記。
//新的壓縮座標是vector中的下標。
int aa=lower_bound(xx.begin(),xx.end(),a[i])-xx.begin();
int bb=lower_bound(xx.begin(),xx.end(),b[i])-xx.begin();
int cc=lower_bound(yy.begin(),yy.end(),c[i])-yy.begin();
for(int x=aa;x<bb;x++){
u[x][cc-1]=1;
d[x][cc]=1;
}
}
for(int i=0;i<m;i++){
int dd=lower_bound(xx.begin(),xx.end(),D[i])-xx.begin();
int ee=lower_bound(yy.begin(),yy.end(),e[i])-yy.begin();
int ff=lower_bound(yy.begin(),yy.end(),f[i])-yy.begin();
for(int y=ee;y<ff;y++){
l[dd][y]=1;
r[dd-1][y]=1;
}
}
//找到牛位置
int x=lower_bound(xx.begin(),xx.end(),0)-xx.begin();
int y=lower_bound(yy.begin(),yy.end(),0)-yy.begin();
//用左下角方格做起點
bfs(x-1,y-1);
if(vis[0][0]){
printf("INF\n");return 0;
}
ll ans=0;
for(int i=0;i<xx.size()-1;i++){
for(int j=0;j<yy.size()-1;j++){
if(vis[i][j]) ans+=(ll)(xx[i+1]-xx[i])*(yy[j+1]-yy[j]);
}
}
printf("%lld\n",ans);
}