公式

D - Integer-duplicated Path 解説 by en_translator


If you start over for each \(k\) to scan from vertex \(1\) to vertex \(k\), it would costs \(O(N^2)\) time, exceeding the execution time limit.
We need some bulk process for all paths to find the answers. What can we do?

Consider the following Depth-First Search (DFS):

  • Start with entering vertex \(1\).
  • When entering vertex \(v\), add information about vertex \(v\).
  • When exiting from vertex \(v\), remove the information about vertex \(v\).

This way, at the points you have entered vertex \(v\) and added information about vertex \(v\), the information about the vertex set formed the path from vertex \(1\) to \(v\) is accumulated.

We will find the answer to the original problem using this fact:

  • Maintain an initially empty set \(S\) and a variable \(cnt\) initialized with \(0\).
  • Entering vertex \(v\):
    • If \(S\) already contains \(A_v\), add \(1\) to \(cnt\).
      • (When vertex \(1\) is regarded as the root) an ancestor already contains \(A_v\), so we memorize that fact.
    • Otherwise, insert \(A_v\) to \(S\).
      • \(A_v\) is not contained in the ancestors of \(v\), so we record it so that descendants can detect the existence of \(A_v\).
      • Afterward, print Yes if \(cnt>0\), and No otherwise.
  • Exiting from vertex \(v\):
    • If \(A_v\) was inserted to \(S\) on entering, remove \(A_v\).
      • In this case, \(v\) is not contained in the ancestors of \(A_v\).
    • Otherwise, subtract \(1\) from \(cnt\).
      • In this case, there is still an \(A_v\) in the ancestors of \(v\), but we need to remove the trigger for the duplicate detection at vertex \(v\).

For example, if you use set of C++, the time complexity becomes \(O(N \log N)\).

This idea can be pushed forward to the concept called Euler Tour (the article is in Japanese).

Sample code (C++):

#include<bits/stdc++.h>

using namespace std;

int n;
vector<int> a;
vector<vector<int>> g;

int cnt=0;
set<int> s;
vector<int> ans;

void dfs(int v,int pv){
  int del=0;
  if(s.find(a[v])!=s.end()){del=1;}
  else{s.insert(a[v]);}
  cnt+=del;
  if(cnt>0){ans[v]=1;}
  else{ans[v]=0;}
  for(auto &nx : g[v]){
    if(nx==pv){continue;}
    dfs(nx,v);
  }
  cnt-=del;
  if(del==0){
    s.erase(a[v]);
  }
}

int main(){
  cin >> n;
  a.resize(n);
  for(auto &nx : a){cin >> nx;}
  g.resize(n);
  for(int i=1;i<n;i++){
    int u,v;
    cin >> u >> v;
    u--; v--;
    g[u].push_back(v);
    g[v].push_back(u);
  }
  ans.resize(n);
  dfs(0,-1);
  for(auto &nx : ans){
    if(nx){cout << "Yes\n";}
    else{cout << "No\n";}
  }
  return 0;
}

投稿日時:
最終更新: