「國慶訓練」Bomb(HDU-5934)
阿新 • • 發佈:2018-10-10
+= edge memset 說了 ring begin 就是 連通 cas
題意
給定\(n\)個炸彈,每個炸彈的坐標與代價與影響範圍給定,炸彈會引爆影響範圍內其他所有炸彈。求引爆所有炸彈的最小代價。
分析
先做\(n^2\)的循環,然後建圖,對\(i\)能引爆\(j\)建邊\((i,j)\)。然後對這個圖求強連通分量並縮點,構成新的有向無環的森林。定義每個強連通分量的cost為其中包含的點的最小cost,然後把新森林中所有入度為0的點的cost加起來求和即可(由於無環,所以從任何入度不為0的點往回走,必然終止於一個入度為0的點)。
代碼
#include <iostream> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> #include <vector> #include <set> #include <map> #include <queue> #include <stack> #define MP make_pair #define PB push_back #define fi first #define se second #define ZERO(x) memset((x), 0, sizeof(x)) #define ALL(x) (x).begin(),(x).end() #define rep(i, a, b) for (int i = (a); i <= (b); ++i) #define per(i, a, b) for (int i = (a); i >= (b); --i) #define QUICKIO ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); #define MS(x,y) memset(x,y,sizeof(x)) #define int ll using namespace std; typedef long long ll; const int MAXN=1005; vector<int> G[MAXN]; bool mat[MAXN][MAXN]; int n; int pre[MAXN], lowlink[MAXN], sccno[MAXN], dfs_clock, scc_cnt; stack<int> stk; void dfs(int u) { pre[u]=lowlink[u] = ++dfs_clock; stk.push(u); rep(i,0,n-1) { if(!mat[u][i]) continue; int v=i; if(!pre[v]) { dfs(v); lowlink[u]=min(lowlink[u],lowlink[v]); } else if(!sccno[v]) { lowlink[u]=min(lowlink[u],pre[v]); } } if(lowlink[u]==pre[u]) { scc_cnt++; for(;;) { int x=stk.top(); stk.pop(); sccno[x]=scc_cnt; if(x==u) break; } } } void find_scc() { dfs_clock=scc_cnt=0; ZERO(sccno); ZERO(pre); rep(i,0,n-1) if(!pre[i]) dfs(i); } bool nmat[MAXN][MAXN]; vector<pair<int,int> > edges; vector<int> nG[MAXN]; int ncnt=0; void add_edges(int u,int v) { edges.PB(MP(u,v)); nG[u].PB(edges.size()-1); } pair<int,int> pnt[MAXN]; int pntc[MAXN], pntr[MAXN]; inline double dist(int x,int y) { return sqrt((pnt[x].fi-pnt[y].fi)*(pnt[x].fi-pnt[y].fi)+ (pnt[x].se-pnt[y].se)*(pnt[x].se-pnt[y].se)); } int cost[MAXN]; signed main() { int T; scanf("%lld", &T); rep(kase,1,T) { ZERO(nmat); ZERO(mat); scanf("%lld", &n); rep(i,1,n) { int x,y; scanf("%lld%lld%lld%lld", &x,&y, &pntr[i], &pntc[i]); pnt[i]=MP(x,y); } rep(i,1,n) { rep(j,1,n) { if(i==j) continue; double d=pntr[i]-dist(i,j); if(fabs(d)<1e-6 || d>1e-6) { mat[i-1][j-1]=true; } } } /* rep(i,0,n-1) { rep(j,0,n-1) cout<<mat[i][j]<<" "; cout<<endl; } */ find_scc(); memset(cost,0x3f,sizeof(cost)); rep(i,1,n) { cost[sccno[i-1]]=min(cost[sccno[i-1]],pntc[i]); } rep(i,0,n-1) { rep(j,0,n-1) { if(i==j) continue; nmat[sccno[i]][sccno[j]]|= mat[i][j]; } } /* rep(i,1,scc_cnt) { rep(j,1,scc_cnt) cout<<nmat[i][j]<<" "; cout<<endl; } */ ll ans=0; /* rep(i,0,n-1) cout<<sccno[i]<<" "; cout<<endl; rep(i,0,n-1) cout<<cost[i]<<" "; cout<<endl; */ rep(i,1,scc_cnt) { int ok=0; rep(j,1,scc_cnt) { if(i==j) continue; if(nmat[j][i]) { ok++; break; } } if(!ok) { //cout<<i<<" "<<cost[i]<<endl; ans+=cost[i]; } } printf("Case #%lld: %lld\n", kase, ans); } return 0; }
劄記
這題是在一場訓練賽中打的。當時的我們激情卡題兩個半小時23333然後我覺得不行了只能換題,不懂圖論的隊友說了這題可以寫,他覺得是帶權並查集23333我想了一下,這一看就是縮點啊。然後縮點之後沒什麽好辦法,不過也沒浪費時間——他們還在卡題23333過了又是半個小時,他們終於出了另外一題(卡的那個簽到題還是沒出!!!),這個時候還剩下一個半小時了,我想到可以求和入度為0的點即可。然後又過去半個小時(出簽到題啊啊啊啊啊)沒出(- -|||),只好我上寫這題,然後半個小時寫完,5分鐘調試,交上去WA,看了下代碼,改了個long long,過了。後來那個簽到題成功出了(太真實了),我們翻盤大成功,哇哢哢~
「國慶訓練」Bomb(HDU-5934)