[CODE FESTIVAL 2016 Exhibition A]Distance Pairs
題意:有一個未知的邊權為$1$的圖,給定所有點到$1$的最短路$a_i$和到$2$的最短路$b_i$,問是否存在這樣的圖,如果存在,問圖中最少有多少條邊
先考慮$a_i$,有$a_1=0,a_i\neq0(i\neq1)$,對於一條邊$(x,y)$有$|a_x-a_y|\leq1$,對於任意$x\neq1$,存在$(x,y)$使得$a_x-1=a_y$,對$b_i$也有類似的約束
所以,我們將點$i$作為$(a_i,b_i)$畫在平面上,那麽至少要有兩條邊:第一條連到$(a_i-1,b_i-1)$或$(a_i-1,b_i)$或$(a_i-1,b_i+1)$,第二條連到$(a_i-1,b_i-1)$或$(a_i,b_i-1)$或$(a_i+1,b_i-1)$,如果這時找不到可以連邊的點,那麽就無解了(點$1$不用連第一種邊,點$2$不用連第二種邊)
如果可以連,那麽我們構造出了一個有$2n-2$條邊的圖,顯然這個圖是滿足條件的
但我們要最小化圖中邊數,所以考慮尋找盡可能多的可以共用的邊
以下我們將第一種邊稱為$a$邊,第二種邊稱為$b$邊,共用邊只可能是兩種情況:1.對於點$i$,存在$(a_i-1,b_i-1)$,此時$i$的$a$邊和$b$邊可以共用;2.存在$i,j$使得$a_i-1=a_j,b_i+1=b_j$,此時$i$的$a$邊可以和$j$的$b$邊共用
我們把這$2n-2$條邊看成點,如果兩條邊可以共用,在代表它們的點之間連一條邊,求最大匹配即可,雖然是一般圖,但註意到只有那些滿足$a_i+b_i$相等的點$i$引出的邊才可能共用,所以建出來的圖是這樣的(圖來自官方題解)
圖中框代表某個坐標$(a_i,b_i)$上的點,紅點代表$b$邊,藍點代表$a$邊,如果方框內有連邊說明存在$(a_i-1,b_i-1)$
這種分層圖可以從左上往右下貪心選邊求最大匹配,實現時只需維護每個坐標的未匹配點個數即可
總時間復雜度為$O(n\log n)$,感覺這題還是不錯的
#include<stdio.h> #include<map> using namespace std; map<int,map<int,int> >mp,vis; int a[100010],b[100010]; bool ex(int x,int y){ return mp.count(x)&&mp[x].count(y); } int main(){ int n,i,j,s,las,now; scanf("%d",&n); #define wa {puts("-1");return 0;} for(i=1;i<=n;i++){ scanf("%d%d",a+i,b+i); if(i!=1&&a[i]==0)wa if(i!=2&&b[i]==0)wa mp[a[i]][b[i]]++; } for(i=1;i<=n;i++){ if(i!=2){ if(!(ex(a[i]-1,b[i]-1)||ex(a[i],b[i]-1)||ex(a[i]+1,b[i]-1)))wa } if(i!=1){ if(!(ex(a[i]-1,b[i]+1)||ex(a[i]-1,b[i])||ex(a[i]-1,b[i]-1)))wa } } s=0; for(i=1;i<=n;i++){ if(!ex(a[i]-1,b[i]+1)&&!vis[a[i]][b[i]]){ vis[a[i]][b[i]]=1; las=0; for(j=0;ex(a[i]+j,b[i]-j);j++){ now=mp[a[i]+j][b[i]-j]; s+=min(las,now); now-=min(las,now); if(ex(a[i]+j-1,b[i]-j-1)){ s+=now; now=min(las,mp[a[i]+j][b[i]-j]); }else now=mp[a[i]+j][b[i]-j]; las=now; } } } printf("%d",2*n-2-s); }
[CODE FESTIVAL 2016 Exhibition A]Distance Pairs