CodeForces 741D Arpa's letter-marked tree and Mehrdad's Dokhtar-kosh paths (thinking + dsu on tree + prefix XOR)

CF741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths

Probably the most difficult problem of dsu on tree... There are still some places that I haven't figured out. Save it for now.

More detailed ideas: here

Soution:
 Note that the maximum letter is v, that is, there are 22 kinds of characters at most. When you see this data range, I think of using state pressure. So how do you use state pressure in this question? Think of a property: a simple path can be transformed into a palindrome by scrambling, if and only if the number of characters with an odd number of occurrences on this path does not exceed one.
 We record the parity of each character, the even number is 0, the odd number is 1, then for the subtree rooted at x xx, stap sta_pstap​ is the path state from point p to x, and staq sta_qstaq​ is q Point to the path state of x, then obviously the state of the total path is stap sta_pstap​ ^ staq sta_qstaq​, temporarily denoted as ret retret. Obviously, the condition for the path to be legal is: the number of 1 in ret retret ≤ 1 \leq 1≤1 .
 Know how to record, first consider violent solution.
subtask 1: subtask_1:subtask1​: Start searching from the root node x and record the status of all paths. How to find whether this status is XORed with other chains has a legal solution? In fact, it is simple. Considering the particularity of the XOR property, that is, x ^ y = z, then x ^ z = y. Then XOR the current path state and all legal states (obviously only 23), the result is temporarily Record it as now nownow, and then find out whether there is a status of now nownow.
subtask 2: subtask_2:subtask2​:How to get the answer? First find out the depth of all nodes, record it as depi dep_idepi​, then for the above legal state, the answer is depp + depq − 2 ∗ depx dep_p + dep_q-2 * dep_xdepp​+depq​−2∗depx​, you can see depp dep_pdepp​ and depq dep_qdepq​ The larger the better, so for paths with the same state, only the deepest one needs to be recorded, so the complexity is guaranteed.
 The above-mentioned violent solution is because the state array needs to be cleared and re-recorded every time, the complexity is O (23 ∗ n 2) O(23 * n^2)O(23∗n2), which cannot be passed. Thinking of the particularity of the tree, the problem is again Statically without modification, adding heuristic merging on the tree, the complexity is reduced to O (23 ∗ n ∗ log 2 n) O(23 * n * log_2n)O(23∗n∗log2​n), and it can be passed.

The code has to think about how to write it.

The internal approach to clarify the truth feels that I don’t think it is very transparent, so I won’t be wrong. Wait for me to review

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<unordered_map>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=5e5+1000;
typedef int LL;
LL siz[maxn],son[maxn],dep[maxn],flag=0;
LL xor_sum[maxn];
LL cnt[1<<22];
LL ans[maxn];
vector< pair<LL,LL> >g[maxn];
void predfs(LL u,LL fa){
    siz[u]=1;dep[u]=dep[fa]+1;
    for(LL i=0;i<g[u].size();i++){
        LL v=g[u][i].first;
        LL k=g[u][i].second;
        if(v==fa) continue;
        xor_sum[v]=xor_sum[u]^k;///前缀异或,类似前缀和
        predfs(v,u);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]]){
            son[u]=v;
        }
    }
}
void cal(LL u,LL fa,LL lca,LL val){

        if(cnt[xor_sum[u]]) ans[lca]=max(ans[lca],cnt[xor_sum[u]]+dep[u]-2*dep[lca]);///枚举的根节点作为lca时候的贡献

        for(LL i=0;i<=21;i++) if(cnt[xor_sum[u]^(1<<i)]) ans[lca]=max(ans[lca],cnt[xor_sum[u]^(1<<i)]+dep[u]-2*dep[lca]);
       /// cnt[xor_sum[u]]=max(cnt[xor_sum[u]],dep[u]);
        for(LL i=0;i<g[u].size();i++){
            LL v=g[u][i].first;
            if(v==fa||v==flag) continue;
            cal(v,u,lca,val);
        }
}
void add(LL u,LL fa,LL val){
     cnt[xor_sum[u]]=max(cnt[xor_sum[u]],dep[u]);
     for(LL i=0;i<g[u].size();i++){
        LL v=g[u][i].first;
        if(v==fa||v==flag) continue;
        add(v,u,val);
     }
}
void del(LL u,LL fa,LL val){
     cnt[xor_sum[u]]=0;
     for(LL i=0;i<g[u].size();i++){
        LL v=g[u][i].first;
        if(v==fa||v==flag) continue;
        del(v,u,val);
     }
}
void dfs(LL u,LL fa,bool keep)
{
    for(LL i=0;i<g[u].size();i++){
        LL v=g[u][i].first;
        if(v==fa||v==son[u]) continue;
        dfs(v,u,0);
        ans[u]=max(ans[u],ans[v]);///来自不经过u点的轻儿子的最大值转移
    }
    if(son[u]){
        dfs(son[u],u,1);
        flag=son[u];
        ans[u]=max(ans[u],ans[son[u]]);///来自不经过u点的重儿子的最大值转移
    }
    cnt[xor_sum[u]]=max(cnt[xor_sum[u]],dep[u]);///维护相同状态的路径,只需要记录深度最深的那个
    ans[u]=max(ans[u],cnt[xor_sum[u]]-dep[u]);///维护一端连着u,一端在u的子树的内部的链子转移
    for(LL i=0;i<=21;i++) if(cnt[xor_sum[u]^(1<<i)]) ans[u]=max(ans[u],cnt[xor_sum[u]^(1<<i)]-dep[u]);
    for(LL i=0;i<g[u].size();i++){
        LL v=g[u][i].first;
        if(v==fa||v==flag) continue;
        cal(v,u,u,1);
        add(v,u,1);
    }
   /// cal(u,fa,u,1);
   /// add(u,fa,1);
    ///先计算答案,再更新cnt[]
    flag=0;
    if(keep==0){
        del(u,fa,-1);
    }
}
int main(void)
{
  ///cin.tie(0);std::ios::sync_with_stdio(false);
  ///题目以1为根
  LL n;scanf("%d",&n);

  for(LL i=2;i<=n;i++){
     LL x;char str;scanf("%d %c",&x,&str);
     g[x].push_back({i,(1<<(str-'a'))});
     g[i].push_back({x,(1<<(str-'a'))});
  }
  predfs(1,0);
  dfs(1,0,0);
  for(LL i=1;i<=n;i++){
    printf("%d ",ans[i]);
  }
  printf("\n");
return 0;
}

 

Guess you like

Origin blog.csdn.net/zstuyyyyccccbbbb/article/details/110581783
Recommended