1. 程式人生 > >UVA10720 Graph Construction 度序列可圖性

UVA10720 Graph Construction 度序列可圖性

Luogu傳送門(UVA常年上不去)

題意:求一個度序列是否可變換為一個簡單圖。$\text{序列長度} \leq 10000$


題目看起來很簡單,但是還是有一些小細節需要注意
首先一個簡單的結論:一張圖的所有點的度數之和為偶數,因為每一條邊都會對度數和產生$2$的貢獻。通過這一個結論可以判斷掉很多的非法情況。
當然如果做到這裡就天真地交上去了,在若干秒之後就會給你顯示一個喜慶的$WA$,因為我們還有一個很重要的因素沒有考慮:圖是一個簡單圖。簡單圖意味著不能有重邊和自環,而上面的那個結論不足以判斷圖中是否必定存在重邊和自環。
我們按照如下的思路考慮:因為度數比較大的點最有可能出現重邊和自環,所以我們考慮這些點儘可能地向其他的點連邊後會不會有度數的剩餘。


將給出的序列$a$從大到小排序,設其非$0$元素長度為$p$,考慮一個長度為$i$的字首,這$i$個點之間連邊產生$i \times (i - 1)$的貢獻,而這$i$個點與其餘$(p-i)$個點連邊又產生$i \times(p-i)$的貢獻,也就是說如果$\sum \limits _{k=1} ^i a_k > i \times (p-1)$,則這個序列是不可圖的
然後又激動地交了上去,然後又$WA$了,然後去$Udebug$上找資料,發現了這樣一組資料:

8 1 1 3 4 4 4 6 7

事實上這一組資料是非法的,因為對於$6$和$7$來說,至少要連出$11$條邊,但是實際上最多隻能連出$10$條邊(有兩個$1$),所以上面的演算法有疏漏。疏漏在哪裡呢?是$\sum \limits _{k=1} ^i a_k > i \times (p-1)$的右半邊多算了(比如說上面資料在$i=2$時的兩個$1$的貢獻就多算了)

所以我們考慮:維護一個指標,將比當前的$i$小的數全部丟到一邊,單獨維護它們的和,再加上沒有被丟掉的點的個數$\times i$,與$\sum \limits _{k=1} ^i a_k$作比較,如果大了表示當前序列不合法。這種演算法就可以$AC$這道題了。
為什麼這個題目寫了這麼長的題解qwq

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 inline int read(){
 5     int a = 0;
 6     char c = getchar();
 7     bool f = 0;
 8
while(!isdigit(c)){ 9 if(c == '-') 10 f = 1; 11 c = getchar(); 12 } 13 while(isdigit(c)){ 14 a = (a << 3) + (a << 1) + (c ^ '0'); 15 c = getchar(); 16 } 17 return f ? -a : a; 18 } 19 20 int num[10010]; 21 22 bool cmp(int a , int b){ 23 return a > b; 24 } 25 26 int main(){ 27 #ifndef ONLINE_JUDGE 28 freopen("10720.in" , "r" , stdin); 29 freopen("10720.out" , "w" , stdout); 30 #endif 31 int N; 32 while(N = read()){ 33 bool f = 0; 34 for(int i = 1 ; i <= N ; ++i){ 35 num[i] = read(); 36 if(num[i] & 1) 37 f ^= 1; 38 } 39 sort(num + 1 , num + N + 1 , cmp); 40 int sum = 0 , p = N , lst , add = 0; 41 while(p && !num[p]) 42 --p; 43 lst = p; 44 for(int i = 1 ; !f && i <= lst ; ++i){ 45 sum += num[i]; 46 if(sum - i * (i - 1) - i * (p - i) - add > 0) 47 f = 1; 48 while(p > i && num[p] == i) 49 add += num[p--]; 50 if(p == i) 51 add -= num[++p]; 52 } 53 puts(f ? "Not possible" : "Possible"); 54 } 55 return 0; 56 }