P1084 [NOIP2012 提高組] 疫情控制

rgw2010發表於2024-08-14

思路:

注意到答案有單調性,考慮二分答案。

現在由最優性問題轉換為判定性問題。

我們很容易注意到一個性質:

  • 一個軍隊不停的往上跳是更優的。

  • 因為可以覆蓋住更多的葉子節點。

那麼對於二分答案的 \(mid\),我們只需要求出每一個軍隊在 \(mid\) 時間內能跳到的最高的那個點,然後判定一下是否覆蓋了所有葉子節點即可。

但是這樣會存在一個問題,即若當前這個點為 \(1\) 的兒子,且有多個軍隊跳到,則可能可以支援一下 \(1\) 號點的其它兒子(因為檢查點不能放在 \(1\) 上,則可能還有空餘的時間經過 \(1\) 走到 \(1\) 的其它兒子)。

先來考慮一個簡單的問題:

  • 若已知每個檢查點的位置,如何判斷是否覆蓋了所有葉子節點;設 \(g_i\) 表示 \(i\) 處是否有檢查點。

考慮先對於每個葉子節點標一個號(按照 dfn 序的大小),那麼我們可以搜的時候預處理出 \(l_i,r_i\) 表示 \(i\) 這棵子樹的葉子節點的編號為 \([l_i,r_i]\)

則對於一個檢查點 \(x\),它可以覆蓋 \([l_x,r_x]\) 這個範圍,令這個區間都加 \(1\),最後檢驗一下是否每個葉子節點都被加了至少一次,可以用差分實現。

但是這樣就無法考慮支援問題,因為我們需要知道 \(1\) 號節點的每一個兒子所在子樹的葉子節點是否全部都被覆蓋。

考慮樹形 dp,令 \(dp_u\) 表示點 \(u\) 所在子樹的葉子節點是否都被覆蓋了,則狀態轉移方程為:

\[dp_u = \begin{cases} 1 & g_u = 1 \\ \operatorname{and}\limits_{(u,v) \in E} dp_v & g_u = 0 \end{cases} \]

\(1\) 號點的兒子 \(u\) 滿足 \(dp_u=0\),那麼該點需要支援,將所有需要支援的點按照到 \(1\) 的距離排序。

則對於可以發動支援的點 \(v\),設 \(v\) 到達 \(1\) 後還可以走 \(k\) 的距離(需要按 \(k\) 從小到大排序),那麼我們可以貪心在需要支援的點集中找到距離 \(\le k\) 且距離最大的點,可以用 set 維護插入,刪除,二分操作。

要注意一下細節,即若 \(v\) 不在 \(v\) 上設定檢查點,也可以使得 \(dp_v=1\),那麼我們可以將 \(v\) 上的軍隊全部派出去支援;否則需要留一個軍隊守家,我們可以留剩下能走距離最小的那個軍隊守。

此時還會有一個問題別問我為什麼貪心有這麼多細節,即若這個點上的某一軍隊到不了任何一個需要支援的點且不需要守家,但是可以到一個需要留一個軍隊守家的點,且那個守家的軍隊可以走足夠的距離去支援一個點,那麼可以讓這個點上的軍隊去代替它守家,這樣會更優,稱之為二次支援

考慮在一次支援完後記錄一下所有在守家且可以支援的點和所有無法支援卻不需要守家的點

那麼二次支援一次支援差不多,對於無法支援卻不需要守家的點的點 \(u\),設還可以走 \(k\) 的距離,那麼我們可以在所有在守家且可以支援的點中找到距離 \(\le k\) 且距離最大的點,也可以用 set 維護。

然後再來考慮跳躍的問題:

  • 如何求出一個點在 \(mid\) 時間內能上跳到的最高的點。

很容易想到二分跳的次數然後長鏈剖分求 \(k\) 級祖先,這樣是 \(\log^2 N\) 的,考慮最佳化。

考慮倍增與貪心,令 \(F_{i,j}\) 表示 \(i\)\(2^j\) 級祖先,\(W_{i,j}\) 表示 \(i\) 到它的 \(2^j\) 級祖先的距離,狀態轉移方程為:

\[F_{i,j} = F_{F_{i,j-1},j-1} \]

\[W_{i,j} = W_{i,j-1} + W_{F_{i,j-1},j-1} \]

那麼可以直接貪心從高位到低位列舉二級制位 \(i\),若 \(W_{x,i} \le mid\),則令 \(mid \gets mid - W_{x,i}\),然後令 \(x\) 跳到 \(F_{x,i}\) 處;最後跳到不能再跳即可。

\(N,M\) 同階,則總時間複雜度為 \(O(N \log N \log W)\)

完整程式碼:

#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=5e4+10,M=16;
inline ll read(){
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')
          f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
inline void write(ll x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9)
	  write(x/10);
	putchar(x%10+'0');
}
ll n,m,x,t,l,r,ans=-1;
ll f[N],h[N],dp[N],DP[N];
ll F[N][M],W[N][M];
bool g[N];
vector<ll> T2;
vector<pi> T1;
vector<pi> E[N],G[N];
multiset<ll> S;
multiset<pi> T;
void add(ll u,ll v,ll w){
    E[u].push_back({v,w});
    E[v].push_back({u,w});
}
void dfs1(ll u,ll fa){
    ll son=0;
    if(u!=1){
        for(int i=1;i<M;i++){
            F[u][i]=F[F[u][i-1]][i-1];
            W[u][i]=W[u][i-1]+W[F[u][i-1]][i-1];
        }        
    }
    for(auto t:E[u]){
        ll v=t.fi,w=t.se;
        if(v==fa)
          continue;
        son++;
        if(u==1)
          F[v][0]=v;
        else{
            F[v][0]=u;
            W[v][0]=w;
        }
        dfs1(v,u);
    }
}
void dfs2(ll u,ll fa){
    if(g[u])
      dp[u]=1;
    for(auto t:E[u]){
        ll v=t.fi;
        if(v==fa)
          continue;
        dfs2(v,u);
        if(DP[u]==-1)
          DP[u]=dp[v];
        else
          DP[u]&=dp[v];
    }
    if(DP[u]==-1)
      DP[u]=0;
    if(dp[u]<1)
      dp[u]=DP[u];
}
bool check(ll mid){
    T1.clear(),T2.clear(),S.clear(),T.clear();
    for(int i=1;i<=n;i++){
        g[i]=h[i]=0;
        dp[i]=DP[i]=-1;
        G[i].clear();
    }
    for(int i=2;i<=n;i++){
        if(f[i]){
            x=i,t=mid;
            for(int j=M-1;j>=0;j--){
                if(W[x][j]<=t){
                    t-=W[x][j];
                    x=F[x][j];
                }
            }
            g[x]=1;
            h[x]+=f[i];
            G[x].push_back({t,f[i]});
        }
    }
    dfs2(1,1);
    for(auto t:E[1]){
        ll v=t.fi,w=t.se;
        DP[v]^=1ll;
        if(dp[v])
          continue;
        S.insert(w);
    }
    for(auto t:E[1]){
        bool F=1;
        ll v=t.fi,w=t.se;
        if(!dp[v])
          continue;
        sort(G[v].begin(),G[v].end());
        for(auto p:G[v]){
            ll d=p.fi-w,s=p.se;
            if(F&&DP[v]){
              s--;
              T1.push_back({w,d});
              F=0;
            }
            while(s&&!S.empty()){
                auto it=S.upper_bound(d);
                if(it!=S.begin()){
                    it--;
                    S.erase(it);
                }
                else
                  T2.push_back(d);
                s--;
                h[v]--;
            }
        }
        if(S.empty())
          break;
    }
    if(S.empty())
      return 1;
    else{
        for(auto t:T1)
          if(S.upper_bound(t.se)!=S.begin())
            T.insert(t);
        for(auto v:T2){
            auto it=T.upper_bound({v,1e18});
            if(it!=T.begin()){
                it--;
                t=(*it).se;
                T.erase(it);
                auto it=S.upper_bound(t);
                if(it!=S.begin()){
                    it--;
                    S.erase(it);
                }
            }
        }
        if(S.empty())
          return 1;
        return 0;
    }
}
bool End;
int main(){
    n=read();
    for(int u,v,w,i=1;i<n;i++){
        u=read(),v=read(),w=read();
        add(u,v,w);
    }
    m=read();
    while(m--){
        x=read();
        f[x]++;
    }
    dfs1(1,1);
    l=0,r=5e13;
    while(l<=r){
        ll mid=(l+r)>>1;
        if(check(mid)){
            ans=mid;
            r=mid-1;
        }
        else
          l=mid+1;
    }
    write(ans);
//	cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB";
	return 0;
}

相關文章