2022CCPC綿陽-E-一類圖上DP最佳化

yoshinow2001發表於2024-03-17

link:https://codeforces.com/gym/104065/problem/E
題意:\(n\) 個城市由 \(m\) 條邊連成一張無向圖,每個點所屬連通塊大小至少是2。接下來有 \(q\) 次危機,每次危機恰發生在一個城市 \(x\),需要所有在 \(x\) 城市的所有居民遷移到其他城市(之後可以遷移回來)。第 \(i\) 個城市有 \(a_i\) 人,每個人經過一條邊需要花費 \(w(u,v)\) 的代價。已知接下來發生危機的城市,問所有危機發生後,所有人的總代價最小是多少?
\(1\leq n,m,q\leq 10^5\)


首先對每個人單獨考慮,設 \(f_i\) 是初始在 \(i\) 號點的人經過所有危機需要的花費,則 \(ans=\sum f_i a_i\)
然後很明顯危機發生在 \(x\) 處,\(x\) 的人只需要考慮遷移到某相鄰結點\(y\) ,則花費為 \(w(x,y)+\) 剩下時間裡 \(y\) 需要的花費,所以很明顯 \(f\) 需要倒著計算。設 \(f(i,j)\) 表示\(i\) 號點在 \(j\) 時刻往後考慮需要的最小花費,則

\[f(i,j)=\left\{\begin{aligned}&f(i,j+1),b_j\neq i\\ &\min_{k\in N(i)}f(k,j+1)+w(i,k),b_j=i\end{aligned}\right. \]

乍一看是個 \(O(nq)\) 的DP,每個點從相鄰結點轉移過來。從這題也可以看出這類問題的通用解法:按度數分塊

  • \(deg(x)\leq T\) 的點,暴力列舉鄰域的點
  • \(deg(x)>T\) 的點,這樣的點不超過 \(2m/T\) 個。對於這些點是千萬不能暴力列舉鄰域的,我們可以對每個大度點開一個 multiset,在計算每個小度點時,暴力更新大度點。
  • 縷清一下需要寫的東西:
    • 小度點連著任何點都可以直接暴力更新
    • 大度點連著小度點,需要在小度點的multiset裡直接獲取最小值
    • 大度點連著大度點呢?因為不超過 \(2m/T\) 個,所以在建圖的時候可以直接按度數排序,暴力列舉最大的幾個點,暴力計算
  • 這樣的複雜度:
    • 問到小度點的時候,\(O(T)\) 地求值,\(O(T\log n)\) 地更新大度點
    • 問到大度點的時候,\(O(\log n+2m/T)\) 地求值
    • 均攤複雜度\(O(\log n+T\log n+\frac{2m}{T})\),前面 \(O(\log n)\) 不動,後面 \(T=\sqrt{2m\log n}\) 時取得最小
    • 最終複雜度是 \(O(q\log n+q\sqrt{2m\log n})=O(q\sqrt{2m\log n})\)
  • 這裡會發現,我們複雜度的優越性其實來自於兩者之中,有一個帶 \(\log\) 而另一個可以完全不帶,如果處理兩種點都有 \(\log n\) 的話其實會退化成 \(O(q\sqrt m \log n)\) ,這樣對於 \(10^5\) 的資料基本是跑不過的(除非常數特別小)
  • 這題還可以對時間分治,去掉根號裡的\(\log n\),不過那個方法可能沒什麼普適性
#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+5;
const int MOD=998244353;
const ll INF=0x3f3f3f3f3f3f3f3f;
int n,m,q,T,head[N],deg[N],b[N];
vector<vector<pii>> G;
ll a[N],f[N];
multiset<ll> S[N];

int main(){
    fastio;
    cin>>n>>m>>q;
    G=vector<vector<pii>>(n+1);
    T=sqrt(2*m*log(n+1));
    rep(i,1,n)cin>>a[i];
    rep(i,1,m){
        int u,v,w;cin>>u>>v>>w;
        deg[u]++;G[u].pb(mp(v,w));
        deg[v]++;G[v].pb(mp(u,w));
    }
    rep(i,1,q)cin>>b[i];
    rep(i,1,n)sort(G[i].begin(),G[i].end(),[](pii p1,pii p2){return deg[p1.first]>deg[p2.first];});
    rep(x,1,n)if(deg[x]>T)for(auto [v,w]:G[x])if(deg[v]<=T)S[x].insert(w);
    for(int tc=q;tc>=1;tc--){
        int x=b[tc];
        if(deg[x]<=T){
            for(auto [v,w]:G[x])if(deg[v]>T)S[v].erase(S[v].find(f[x]+w));
            f[x]=INF;
            for(auto [v,w]:G[x])f[x]=min(f[x],f[v]+w);
            for(auto [v,w]:G[x])if(deg[v]>T)S[v].insert(f[x]+w);
        }else{
            f[x]=*S[x].begin();
            for(auto [v,w]:G[x]){
                if(deg[v]<=T)break;
                f[x]=min(f[x],f[v]+w);
            }
        }
    }
    ll ans=0;
    rep(i,1,n)ans=(ans+f[i]%MOD*a[i]%MOD)%MOD;
    cout<<ans;
    return 0;
}

相關文章