我的作法:先隨便選一個點當根轉成有根樹,可以證明,如果有一個點沒被滅人器覆蓋到的話,那選他的k級組先放滅人器是最好的。所以由深度深的點作到深度淺的,每次判斷有沒有滅人器覆蓋到這個點,沒有的話就在k級組先放,有的話就把那個滅人器的個數減一 。另外還有一件蠻直覺的事,就是如果有滿足的滅人器的話,選深度最深的那個,但我不知道怎麼證明@@所以可以想到第一種作法,每作一個點的時候對他DFS,看距離他 k 步以內有沒有滅人器,有的話就更新,找深度最深的,但這樣做是O(n^2)。考慮每個點放了滅人器後,他自己有一個強度 k 的滅人器,他的 i 級組先等於有了一個強度 k-i 的滅人器,所以用個 set 記錄這個點擁有的強度 j 的滅人器有哪些,就可以在O(k^2)的時間內判斷有沒有滅人器覆蓋這個點。另外當然還是要選深度最深的,否則 8-2 測資會錯,獲得WA 90分 (?)
#include<bits/stdc++.h> using namespace std; const int maxn=100000+10 ; vector<int> v[maxn] ; int fa[maxn] ; struct P{int id,val;}a[maxn]; bool cmp(const P &a,const P &b) { return a.val==b.val ? a.id>b.id : a.val>b.val ; } int d[maxn] ; void dfs(int x,int dep) { d[x]=dep ; a[x]=(P){x,dep} ; for(int i=0;i<v[x].size();i++) if(v[x][i]!=fa[x]) fa[v[x][i]]=x , dfs(v[x][i],dep+1) ; } int cnt[maxn] ; set<int> have[maxn][21] ; main() { int n,s,k ; scanf("%d%d%d",&n,&s,&k) ; for(int i=1;i<n;i++) { int x,y ; scanf("%d%d",&x,&y) ; v[x].push_back(y) ; v[y].push_back(x) ; } fa[1]=1 ; dfs(1,0) ; sort(a+1,a+n+1,cmp) ; int ans=0 ; for(int i=1;i<=n;i++) { int x=a[i].id ; int found=-1,dep=-1 ; for(int j=0;j<=k;j++) if(have[x][j].size()) { found=(*have[x][j].begin()) ; dep=d[found] ; break ; } int y=x ; for(int z=1;z<=k;z++) { y=fa[y] ; for(int j=z;j<=k;j++) if(have[y][j].size()) { if(found==-1 || d[*have[y][j].begin()]>dep) found=(*have[y][j].begin()) , dep = d[*have[y][j].begin()]; } } if(found!=-1) { if(!(--cnt[found])) for(int z=0,j=found;z<=k;z++,j=fa[j]) have[j][k-z].erase(found) ; continue ; } cnt[y]=s-1 ; ans++ ; if(s==1) continue ; for(int z=0,j=y;z<=k;z++,j=fa[j]) {have[j][k-z].insert(y) ; if(j==1) break ;} } printf("%d\n",ans) ; }
另外,陳博彰給了個O(nk)的作法,作法是記錄這個點的子樹需要多少距離 i 的滅人器,和能提供多少距離 i 的滅人器,然後盡量晚配對(將需求與供給幾消)兩者。code如下:
#include <cstdio> #include <vector> #include <algorithm> using namespace std; constexpr int max_n = 100000; constexpr int max_k = 20; int n, s, k; vector<int> adj[max_n]; int supply[max_n][max_k + 1]; int demand[max_n][max_k + 1]; int dfs(int x, int p) { int extinguisher = 0; for(int c : adj[x]) if(c != p) { extinguisher += dfs(c, x); for(int i = 0; i < k; i++) { supply[x][i] += supply[c][i + 1]; if(supply[x][i] > n) supply[x][i] = n; demand[x][i + 1] += demand[c][i]; } } demand[x][0] = 1; supply[x][k] = (demand[x][k] + s - 1) / s * s; extinguisher += (demand[x][k] + s - 1) / s; for(int i = 0; i <= k; i++) { int c = min(supply[x][i], demand[x][i]); supply[x][i] -= c; demand[x][i] -= c; } for(int i = 0; i < k; i++) { int c = min(supply[x][i + 1], demand[x][i]); supply[x][i + 1] -= c; demand[x][i] -= c; } return extinguisher; } int main(){ scanf("%d %d %d", &n, &s, &k); for(int i = 0; i < n - 1; i++) { int a, b; scanf("%d %d", &a, &b); a--; b--; adj[a].push_back(b); adj[b].push_back(a); } int extinguisher = dfs(0, -1); for(int i = 0; i <= k; i++) for(int j = 0; j <= i; j++) { int c = min(supply[0][i], demand[0][j]); supply[0][i] -= c; demand[0][j] -= c; } extinguisher += (accumulate(demand[0], demand[0] + k + 1, 0) + s - 1) / s; printf("%d\n", extinguisher); }
沒有留言:
張貼留言