QOJ6958-複雜的雙樹上問題以及簡單的解決方式

HarlemBlog發表於2024-11-21

題面

原題連結

思路

我們考慮如何判斷一對 \(T_1,T_2\) 是否合法。

首先,我們可以發現 \(T_2\) 上的邊權只能有至多一組合法解,這是因為對於任意一條邊連線 \(u,v\),它的邊權必然是 \(dis_1(u,v)\),所以事實上我們是沒有許可權給 \(T_2\) 任意賦權的,這樣題目就簡單了一些。

那麼,我們如何判定每一對 \(u,v\) 均滿足 \(dis_1(u,v)=dis_2(u,v)\) 呢?直接跑最短路顯然不現實,那麼就考慮轉化問題了。

假設 \(T_2\)\(u\rightarrow v\) 的簡單路徑依次經過了 \(v_1\sim v_k\)(包括 \(u\)\(v\)),那麼 \(dis_2(u,v)= \sum\limits_{i=1}^{k-1}dis_2(v_i,v_{i+1})\),因為任意 \(u,v\) 均滿足 \(dis_1(u,v)=dis_2(u,v)\),所以 \(dis_2(u,v)=\sum\limits_{i=1}^{k-1}dis_1(v_i,v_{i+1})\),在合法條件下,這一式子必然成立。

\(\sum\limits_{i=1}^{k-1}dis_1(v_i,v_{i+1})\) 這個式子的內涵,就是 \(T_1\) 上一條 \(v_1\rightarrow v_2\rightarrow v_3\rightarrow\cdots\rightarrow v_k\) 的非簡單路徑的邊權之和,注意,這裡經過的邊權會被計算多次,經過幾次算幾次。

那麼 \(dis_1(u,v)=dis_2(u,v)\) 就轉化成了 \(dis_1(u,v)=\sum\limits_{i=1}^{k-1}dis_1(v_i,v_{i+1})\),與 \(T_2\) 無關了。

如果上面這個式子滿足,不難想到如果一條邊在左右兩式中貢獻次數不同,其邊權只能為 \(0\)。形式化地說,記在右式中與左式中\(e\) 分別被經過了 \(p_e,q_e\) 次,那麼這充要於對於所有 \(p_e\neq q_e\)\(e\)\(w_e=0\)

下面我們引出一個重要性質\(p_e\equiv q_e(\bmod 2)\),並且 \(p_e\ge q_e\),發現 \(p_e\neq q_e\) 充要於 \(p_e\ge 2\),那麼 \(p_e\ge 2\) 的邊就很好刻畫了:它必然在一對簡單路徑 \(i\neq j\)\(path(a_i,a_{i+1})\)\(path(a_j,a_{j+1})\) 的交邊上。

所以,\(dis_1(u,v)=dis_2(u,v)\) 充要於\(\forall 1\le i<j<k,\ path(a_i,a_{i+1}),path(a_j,a_{j+1})\) 相交的部分邊權均為 \(0\)

更進一步, \((T_1,T_2)\) 合法充要於對所有 \(T_2\) 上的邊二元組 \(e_1\neq e_2\)\(path_1(u_{e_1},v_{e_1}),path_1(u_{e_2},v_{e_2})\) 交的部分邊權均為 \(0\)

  • 必要性:如果存在一對邊二元組 \(e_1\neq e_2\)\(path_1(u_{e_1},v_{e_1}),path_1(u_{e_2},v_{e_2})\) 交的部分邊權不為 \(0\),因為 \(T_2\) 上必然有存在 \(path_2(u,v)\) 內包含 \(e_1,e_2\),那麼 \(dis_1(u,v)\neq dis_2(u,v)\),樹不合法。
  • 充分性:讀者自證不難。

然後考慮一個很簡單的轉換,一條邊為兩條路徑的交,那麼它被這兩條路徑覆蓋。

最後,判斷方法就呼之欲出了:對於 \(T_2\) 上的每一條邊 \((u,v)\),將 \(T_1\)\(path_1(u,v)\) 上所有邊覆蓋次數 \(+1\),最後假如 \(T_1\) 上存在邊被覆蓋多次且其邊權不為 \(0\),則不可行。若所有邊均合法,則可行。

實現

不難發現,我們只需要修改所有使得 \((T_1,T_2)\) 不合法的邊就可以了。

更具體地,修改次數就是被覆蓋多次且邊權不為 \(0\)\(T_1\) 上的邊的數量。

如果使用樹上差分計算所有邊的被覆蓋次數,時間複雜度為 \(O(n\log n)\) 的。

同時,考慮到我們只需要判定一條邊是否被覆蓋至少兩次,使用 \(O(n)-O(1)\) LCA 可以做到 \(O(n)\)。當然這並不是必要的,樹上差分已經足以透過這道題了。

程式碼

#include<bits/stdc++.h>
using namespace std;

const int N=1e6+5,T=20;

int n;
int f[N][T+1];
int v[N];
vector<int> ed[N];
int dp[N];
int cf[N];
int ans;

void dfsi(int now){
    dp[now]=dp[f[now][0]]+1;
    for(auto nxt:ed[now]){
        dfsi(nxt);
    }
}

void binit(){
    for(int t=1;t<=T;t++){
        for(int i=1;i<=n;i++){
            f[i][t]=f[f[i][t-1]][t-1];
        }
    }
}

int lca(int a,int b){
    if(dp[a]>dp[b])swap(a,b);
    for(int t=T;t>=0;t--){
        if(dp[f[b][t]]>=dp[a])b=f[b][t];
    }
    if(a==b)return a;
    for(int t=T;t>=0;t--){
        if(f[a][t]!=f[b][t]){
            a=f[a][t];
            b=f[b][t];
        }
    }
    return f[a][0];
}

void dfss(int now){
    for(auto nxt:ed[now]){
        dfss(nxt);
        cf[now]+=cf[nxt];
        if(cf[nxt]>1&&v[nxt]>0)ans++;
    }
}

void solve(){
    cin>>n;
    f[1][0]=1;
    for(int i=1;i<=n;i++)ed[i].clear();
    for(int i=1;i<=n;i++)cf[i]=0;
    for(int i=2;i<=n;i++)cin>>f[i][0];
    for(int i=2;i<=n;i++){
        cin>>v[i];
        ed[f[i][0]].push_back(i);
    }
    dfsi(1);
    binit();
    for(int i=2,j;i<=n;i++){
        cin>>j;
        int k=lca(i,j);
        cf[k]-=2;
        cf[i]++;cf[j]++;
    }
    ans=0;
    dfss(1);
    cout<<ans<<"\n";
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t;cin>>t;
    while(t--)solve();
    return 0;
}

相關文章