QOJ6958-复杂的双树上问题以及简单的解决方式

题面

原题链接

思路

我们考虑如何判断一对 \(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;
}
posted @ 2024-11-21 19:46  HarlemBlog  阅读(5)  评论(0编辑  收藏  举报