結果
問題 |
No.1843 Tree ANDistance
|
ユーザー |
|
提出日時 | 2025-06-10 23:02:45 |
言語 | C++23 (gcc 13.3.0 + boost 1.87.0) |
結果 |
AC
|
実行時間 | 415 ms / 2,000 ms |
コード長 | 5,534 bytes |
コンパイル時間 | 4,190 ms |
コンパイル使用メモリ | 296,684 KB |
実行使用メモリ | 12,264 KB |
最終ジャッジ日時 | 2025-06-10 23:03:02 |
合計ジャッジ時間 | 11,756 ms |
ジャッジサーバーID (参考情報) |
judge4 / judge3 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
other | AC * 38 |
ソースコード
#include <bits/stdc++.h> using namespace std; using ll = long long; using str = string; #define re(n) for(int _=0;_<int(n);_++) #define rep(i,n) for(int i=0; i<int(n); i++) #define rep1(i,s,n) for(int i=int(s); i<int(n); i++) #define rep2(i,s,n,a) for (ll i = a; i < ll(n); i += (a)) #define per(i,n) for(int i=int(n)-1; i>=0; i--) #define per1(i,s,n) for(int i=int(n)-1; i>=s; i--) #define all(x) x.begin(), x.end() #define SORT(v) sort(all(v)) #define REV(v) reverse(all(v)) #define MIN(v) *min_element(all(v)) #define MAX(v) *max_element(all(v)) #define elif else if #define len(x) ll(x.size()) #define _GLIBCXX_DEBUG template<typename T> inline bool chmax(T &a, T b) { return ((a < b) ? (a = b, true) : (false)); } template<typename T> inline bool chmin(T &a, T b) { return ((a > b) ? (a = b, true) : (false)); } template<class T>void vcin(vector<T> &n){for(int i=0;i<int(n.size());i++) cin>>n[i];} template<class T>void vcout(vector<T> &n){for(int i=0;i<int(n.size());i++){cout<<n[i]<<" ";}cout<<endl;} using vi = vector<int>; using vvi = vector<vi>; using pii = pair<int, int>; using vl = vector<ll>; using vvl = vector<vl>; using pll = pair<ll, ll>; using vpi = vector<pii>; using vpl = vector<pll>; using vc = vector<char>; using vs = vector<str>; using quei = deque<int>; using quel = deque<ll>; #pragma GCC target("avx2") #pragma GCC optimize("O3") #pragma GCC optimize("unroll-loops") int main() { // 高速な入出力 ios_base::sync_with_stdio(false); cin.tie(NULL); // 問題で指定される法(剰余) const ll mod = 1e9 + 7; int n; cin >> n; // 隣接リストでグラフ(木)を表現します // g[u] には、頂点uに接続する頂点vと辺の重みcのペア {v, c} が格納されます vector<vector<pair<int, int>>> g(n); for (int i = 0; i < n - 1; ++i) { int u, v, c; cin >> u >> v >> c; // 1-based indexから0-based indexに変換 u--; v--; g[u].push_back({v, c}); g[v].push_back({u, c}); } // DFS(深さ優先探索)で木の走査順序(後行順)を決定します // これにより、動的計画法で親を計算する際に、子の計算が完了していることを保証します vector<int> order; order.reserve(n); vector<int> st; st.push_back(0); // 頂点0から探索開始 vector<bool> seen(n, false); seen[0] = true; while (!st.empty()) { int u = st.back(); st.pop_back(); order.push_back(u); for (auto const& edge : g[u]) { int v = edge.first; if (seen[v]) { continue; } seen[v] = true; st.push_back(v); } } // DFSで得られる順序は先行順なので、逆順にして後行順にします reverse(order.begin(), order.end()); ll ans = 0; // 0から29までの各ビットについて計算を独立して行います for (int i = 0; i < 30; ++i) { // dp[u]: i番目のビットが立った辺のみで構成される、uのサブツリー内で始まりuで終わるパスの数 // -1で初期化し、未訪問の親と計算済の子を区別します vector<ll> dp(n, -1); ll p2 = 1LL << i; // 2^i を事前に計算 // 後行順で各頂点を処理 for (int u : order) { // tmp: uの各子vから計算されたパス数(ret)を格納するリスト vector<ll> tmp; tmp.reserve(g[u].size()); for (auto const& edge : g[u]) { int v = edge.first; int c = edge.second; // dp[v]が-1なら、vはuの親なのでスキップ if (dp[v] == -1) { continue; } ll ret = 0; // 辺(u,v)の重みのi番目のビットが1の場合 if ((c >> i) & 1) { // vのサブツリーからvへ至るパス(dp[v]個)をuまで延長したものと、 // vから始まる新しいパス{v,u}の1つを合わせて、uで終わるパスの数とする ret = dp[v] + 1; } // i番目のビットが0なら、パスは途切れるので ret = 0 tmp.push_back(ret); } ll S = 0; // tmpの合計 ll t = 0; // tmpの各要素の二乗和 for (ll ret_val : tmp) { // 1. uで終わるパスの貢献 // ret_val個の各パスは、最終的な答えに 2^i だけ貢献します ans = (ans + (ret_val % mod * (p2 % mod)) % mod) % mod; // 2. uを通過するパスの計算のための準備 // Pythonの任意精度整数と同様に、64ビット整数の最大値を超えない範囲で計算 S += ret_val; t += ret_val * ret_val; } // 2. uを通過するパスの貢献 // 異なる2つのサブツリーから来たパスをuで繋ぐ場合の数 (Σ ret_j * ret_k) は // (S^2 - t) / 2 で計算できます ll term_val = (S * S - t) / 2; // この貢献分も答えに加算 ans = (ans + (term_val % mod * (p2 % mod)) % mod) % mod; // uのDPテーブルを更新 dp[u] = S; } } cout << ans << endl; return 0; }