[BZOJ 1124][POI 2008] 枪战 Maf
1124: [POI2008]槍戰Maf
Time Limit:?10 Sec??Memory Limit:?162 MBSubmit:?659??Solved:?259
[Submit][Status][Discuss]
Description
有n個人,每個人手里有一把手槍。一開始所有人都選定一個人瞄準(有可能瞄準自己)。然后他們按某個順序開槍,且任意時刻只有一個人開槍。因此,對于不同的開槍順序,最后死的人也不同。
Input
輸入n人數<1000000 每個人的aim
Output
你要求最后死亡數目的最小和最大可能
Sample Input
82 3 2 2 6 7 8 5
Sample Output
3 5不看數據范圍可能看起來像是縮點樹歸的樣子, 但是平常的 $O(n^2)$ 樹歸在這里顯然需要 $\text{真-}Owys$ 才能過( $\text{-}Owys$大法好, $n^2$ 過 $5 \times 10^5$ )(霧
然后按照圖論題的一般套路就該找規律了w(找不到規律就成了神tm不可做題了)
經過一波 $Tarjan$ 縮點后再YY我們可以得到以下結論:
首先對于最大死亡數來說:
當 $SCC$ 為一個點的最大死亡數在存在入邊時為$1$, 否則為 $0$.
當 $SCC$ 為一個環的最大死亡數在存在入邊時為 $SCC$ 中的結點數 $size$ , 否則為 $size-1$ (有一個點是無法殺死的)
當環存在入邊時還要加上連入的環的結點數再減去其中的零入度結點(因為零入度結點不可能死亡).
然后是最小死亡數:
首先零入度結點必定無法殺死, 而零入度結點的目標必死. 所以我們可以在預處理時將零入度結點壓入一個隊列, 然后刪除它和它的目標, 更新答案, 并將刪掉的結點的目標的入度減去 $1$ . 因為殺死必死的人后可能它的目標的入度變成 $0$ 然后變成新的存活結點. 新的存活結點入隊. 如此處理直至隊列為空.
然后我們可以發現所有的鏈都會在這個過程中被處理掉, 也就是說圖里應該刪得只剩下幾坨環了. 然后這時我們可以發現對于環來說, 若環中結點數為 $s$ , 則至少要死去 $\left \lceil \frac {s}{2} \right \rceil$ 個結點. (每次剛好隔開殺手, 隔一個死一個w)
如此處理即可求出最終解.
辣雞分類討論吃棗藥丸(╯‵□′)╯︵┻━┻(霧
參考代碼
GitHub
1 #include <stack> 2 #include <queue> 3 #include <cstdio> 4 #include <cstring> 5 #include <cstdlib> 6 #include <iostream> 7 #include <algorithm> 8 9 const int MAXN=1000010; 10 11 int n; 12 int scc; 13 int Time; 14 int maxA; 15 int minA; 16 int dfn[MAXN]; 17 int low[MAXN]; 18 int size[MAXN]; 19 int belong[MAXN]; 20 int target[MAXN]; 21 int inDegree[MAXN]; 22 int inDegreeSCC[MAXN]; 23 24 bool visited[MAXN]; 25 26 std::stack<int> s; 27 28 void Initialize(); 29 void DFS(int); 30 31 int main(){ 32 std::queue<int> live; 33 Initialize(); 34 for(int i=1;i<=n;i++){ 35 if(inDegree[i]==0) 36 live.push(i); 37 } 38 while(!live.empty()){ 39 int top=live.front(); 40 live.pop(); 41 visited[top]=true; 42 if(visited[target[top]]) 43 continue; 44 minA++; 45 visited[target[top]]=true; 46 if((--inDegree[target[target[top]]])==0&&!visited[target[target[top]]]) 47 live.push(target[target[top]]); 48 } 49 for(int i=1;i<=n;i++){ 50 int cnt=0; 51 if(!visited[i]){ 52 int x=i; 53 do{ 54 visited[x]=true; 55 cnt++; 56 x=target[x]; 57 }while(x!=i); 58 } 59 if(cnt>0) 60 minA+=(cnt+1)/2; 61 } 62 memset(visited,0,sizeof(visited)); 63 for(int i=1;i<=n;i++){ 64 if(dfn[i]==0) 65 DFS(i); 66 } 67 for(int i=1;i<=n;i++){ 68 if(target[i]==i||belong[i]!=belong[target[i]]) 69 inDegreeSCC[belong[target[i]]]++; 70 } 71 for(int i=1;i<=scc;i++){ 72 if(inDegreeSCC[i]>0) 73 maxA+=size[i]; 74 else 75 maxA+=size[i]-1; 76 } 77 printf("%d %d\n",minA,maxA); 78 return 0; 79 } 80 81 void DFS(int root){ 82 dfn[root]=low[root]=++Time; 83 visited[root]=true; 84 s.push(root); 85 if(visited[target[root]]){ 86 low[root]=std::min(low[root],dfn[target[root]]); 87 } 88 else if(dfn[target[root]]==0){ 89 DFS(target[root]); 90 low[root]=std::min(low[root],low[target[root]]); 91 } 92 if(low[root]==dfn[root]){ 93 scc++; 94 int top; 95 do{ 96 top=s.top(); 97 belong[top]=scc; 98 size[scc]++; 99 visited[top]=false; 100 s.pop(); 101 }while(top!=root); 102 } 103 } 104 105 inline void Initialize(){ 106 scanf("%d",&n); 107 for(int i=1;i<=n;i++){ 108 scanf("%d",target+i); 109 inDegree[target[i]]++; 110 } 111 } Backup?
轉載于:https://www.cnblogs.com/rvalue/p/7275511.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的[BZOJ 1124][POI 2008] 枪战 Maf的全部內容,希望文章能夠幫你解決所遇到的問題。