結果
問題 | No.898 tri-βutree |
ユーザー | NyaanNyaan |
提出日時 | 2019-10-04 21:35:47 |
言語 | C++14 (gcc 12.3.0 + boost 1.83.0) |
結果 |
AC
|
実行時間 | 153 ms / 4,000 ms |
コード長 | 8,138 bytes |
コンパイル時間 | 1,897 ms |
コンパイル使用メモリ | 181,804 KB |
実行使用メモリ | 24,320 KB |
最終ジャッジ日時 | 2024-11-08 21:54:45 |
合計ジャッジ時間 | 6,327 ms |
ジャッジサーバーID (参考情報) |
judge4 / judge2 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 81 ms
24,320 KB |
testcase_01 | AC | 2 ms
5,248 KB |
testcase_02 | AC | 2 ms
5,248 KB |
testcase_03 | AC | 2 ms
5,248 KB |
testcase_04 | AC | 2 ms
5,248 KB |
testcase_05 | AC | 2 ms
5,248 KB |
testcase_06 | AC | 2 ms
5,248 KB |
testcase_07 | AC | 142 ms
13,824 KB |
testcase_08 | AC | 137 ms
13,824 KB |
testcase_09 | AC | 137 ms
13,824 KB |
testcase_10 | AC | 141 ms
13,824 KB |
testcase_11 | AC | 139 ms
13,824 KB |
testcase_12 | AC | 138 ms
13,824 KB |
testcase_13 | AC | 145 ms
13,824 KB |
testcase_14 | AC | 137 ms
13,824 KB |
testcase_15 | AC | 147 ms
13,824 KB |
testcase_16 | AC | 142 ms
13,824 KB |
testcase_17 | AC | 142 ms
13,824 KB |
testcase_18 | AC | 145 ms
13,824 KB |
testcase_19 | AC | 141 ms
13,824 KB |
testcase_20 | AC | 145 ms
13,824 KB |
testcase_21 | AC | 153 ms
13,824 KB |
ソースコード
#include <bits/stdc++.h> #define whlie while #define pb push_back #define eb emplace_back #define fi first #define se second #define rep(i,N) for(int i = 0; i < (N); i++) #define repr(i,N) for(int i = (N) - 1; i >= 0; i--) #define rep1(i,N) for(int i = 1; i <= (N) ; i++) #define repr1(i,N) for(int i = (N) ; i > 0 ; i--) #define each(x,v) for(auto& x : v) #define all(v) (v).begin(),(v).end() #define sz(v) ((int)(v).size()) #define vrep(v,it) for(auto it = v.begin(); it != v.end(); it++) #define vrepr(v,it) for(auto it = v.rbegin(); it != v.rend(); it++) #define ini(...) int __VA_ARGS__; in(__VA_ARGS__) #define inl(...) ll __VA_ARGS__; in(__VA_ARGS__) #define ins(...) string __VA_ARGS__; in(__VA_ARGS__) using namespace std; void solve(); using ll = long long; using vl = vector<ll>; using vi = vector<int>; using vvi = vector< vector<int> >; constexpr int inf = 1001001001; constexpr ll infLL = (1LL << 61) - 1; struct IoSetupNya {IoSetupNya() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(15); cerr << fixed << setprecision(7);} } iosetupnya; template<typename T, typename U> inline bool amin(T &x, U y) { return (y < x) ? (x = y, true) : false; } template<typename T, typename U> inline bool amax(T &x, U y) { return (x < y) ? (x = y, true) : false; } template<typename T, typename U> ostream& operator <<(ostream& os, const pair<T, U> &p) { os << p.first << " " << p.second; return os; } template<typename T, typename U> istream& operator >>(istream& is, pair<T, U> &p) { is >> p.first >> p.second; return is; } template<typename T> ostream& operator <<(ostream& os, const vector<T> &v) { int s = (int)v.size(); rep(i,s) os << (i ? " " : "") << v[i]; return os; } template<typename T> istream& operator >>(istream& is, vector<T> &v) { for(auto &x : v) is >> x; return is; } void in(){} template <typename T,class... U> void in(T &t,U &...u){ cin >> t; in(u...);} void out(){cout << "\n";} template <typename T,class... U> void out(const T &t,const U &...u){ cout << t; if(sizeof...(u)) cout << " "; out(u...);} template<typename T>void die(T x){out(x); exit(0);} #ifdef NyaanDebug #include "NyaanDebug.h" #define trc(...) do { cerr << #__VA_ARGS__ << " = "; dbg_out(__VA_ARGS__);} while(0) #define trca(v,N) do { cerr << #v << " = "; array_out(v , N);cout << endl;} while(0) #else #define trc(...) #define trca(...) int main(){solve();} #endif using P = pair<int,int>; using vp = vector<P>; constexpr int MOD = /** 1000000007; //*/ 998244353; /////////////////////////////////////////////////////////// template< typename T > struct edge { int src, to; T cost; edge(int to) : src(-1) , to(to) , cost(0){} edge(int to, T cost) : src(-1), to(to), cost(cost) {} edge(int src, int to, T cost) : src(src), to(to), cost(cost) {} edge &operator=(const int &x) { // edge e = n; とできる(e.to = n) to = x; return *this; } // int()をオーバーロードすることで重み付きグラフと普通のグラフでライブラリを共用できる operator int() const { return to; } // v[e]とできる ( v[e.to]に同じ ) }; // sort用 とりあえずcostの小さいほうが左としているが適宜書き換えて使いましょう /** template<typename T> bool operator <(const edge<T>& e1,const edge<T>& e2){ return e1.cost < e2.cost; }//*/ template< typename T > using Edges = vector< edge< T > >; template< typename T > using WG = vector< Edges< T > >; template< typename T> using UWG = vector< vector <int> >; // HL分解 // 木の内部の任意の2点間をO(log_V)個に分割するデータ構造。 // これを用いることで木に対してセグ木やbitなどのデータ構造を用いることが出来る。 // 以下、内部のデータの説明。(頂点0を根とした。) // size[i] ... iの部分木の要素数 depth[i] ... iの深さ(depth[0] = 0) // nxt[i] ... vから根に向かって伸びるheavy pathの終着点(nxt[0] = 0) // in[i],out[i] ... オイラーツアーした時の順番。 // [ in[i] , out[i] )で部分木へのクエリとなる。 // [ in[nxt[i]] , in[v] ]でheavy pathへのクエリとなる。 // par[i] ... iの親ノード。 (par[0] = 0) // verify // ABC133F https://atcoder.jp/contests/abc133/submissions/6357522 // AOJ-GRL-5C http://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=3742268#1 // AOJ-GRL-5D https://onlinejudge.u-aizu.ac.jp/status/users/NyaanNyaan/submissions/1/GRL_5_D/judge/3742298/C++14 // AOJ-GRL-5E https://onlinejudge.u-aizu.ac.jp/status/users/NyaanNyaan/submissions/1/GRL_5_E/judge/3742347/C++14 WG<ll> g; struct HLD{ int idx; vector<int> size, depth, in, out, nxt, par; HLD(int N,int root = 0):idx(0),size(N,0),depth(N,0),in(N,-1), out(N,-1),nxt(N,0),par(N,0){ dfs_sz(root); trc(size); trc(depth); dfs_hld(root); trc(in); trc(out); trc(nxt);trc(par); } void build(int root){ dfs_sz(root); dfs_hld(root); } // 構築 void dfs_sz(int cur){ size[cur] = 1; for(auto &dst: g[cur]){ if(dst == par[cur]) { if( g[cur].size() >= 2 && int(dst) == int(g[cur][0]) ) swap(g[cur][0] , g[cur][1]); else continue; } depth[dst] = depth[cur] + 1; par[dst] = cur; dfs_sz(dst); size[cur] += size[dst]; if(size[dst] > size[g[cur][0]]){ swap(dst , g[cur][0]); } } } void dfs_hld(int cur){ in[cur] = idx++; for(auto dst: g[cur]){ if(dst == par[cur]) continue; nxt[dst] = ( int(dst) == int(g[cur][0]) ? nxt[cur] : int(dst) ); dfs_hld(dst); } out[cur] = idx; } // 辺クエリ // aからbへの辺(aが根の側,bが葉の側)の持つ値をstのdata[b]に代入しているとする。 // (すなわち、辺のデータを葉の方の頂点に移し替える。) // (注: 例外処理のためdata[0] = fの単位元とする。) // この時、st.query(u,v + 1)はpar[u]からvまでの辺へのクエリとなる。 // (注: st.query()は引数に半開区間をため第二引数のvに1を足している。) // これをもとに[u , v]の辺へのクエリを実装する。 template<typename F> void edge_query(int u,int v,const F& f){ while(1){ if(in[u] > in[v]) swap(u,v); trc(u,v); if(nxt[u] != nxt[v]){ f(in[nxt[v]] , in[v] + 1); v = par[ nxt[v] ]; } else{ trc(u , v,in[u] , in[v]); if(u != v) f(in[u] + 1, in[v] + 1); break; } } } // 頂点クエリ (verifyしていない) // 辺クエリと同様の議論をする。 // たぶん最後の同じheavy pathに載ったところを、 // in[u]+1からin[u]にかえれば正しく動くと思う。 template<typename F> void node_query(int u,int v,const F& f){ while(1){ if(in[u] > in[v]) swap(u,v); trc(u,v); if(nxt[u] != nxt[v]){ f(in[nxt[v]] , in[v] + 1); v = par[ nxt[v] ]; } else{ trc(u , v,in[u] , in[v]); f(in[u], in[v] + 1); break; } } } // 部分木クエリ template<typename F> void sub_edge_query(int u,const F& f){ f(in[u] + 1, out[u]); } template<typename F> void sub_node_query(int u,const F& f){ f(in[u], out[u]); } // 最小共通祖先 int lca(int a,int b){ while(nxt[a] != nxt[b]){ if( in[a] < in[b] ) swap(a,b); a = par[nxt[a]]; } return depth[a] < depth[b] ? a : b; } }; vl memo; void dfs(int cur , int par , ll cost){ if(cur == 0) memo[cur] = 0; else memo[cur] = memo[par] + cost; each(x , g[cur]){ if(x == par) continue; dfs(x , cur , x.cost); } } void solve(){ ini(N); memo.resize(N , 0); g.resize(N); rep(_,N-1){ inl(a,b,c); g[a].eb(b,c); g[b].eb(a,c); } HLD hld(N); hld.build(0); dfs(0 , -1 , 0); ini(Q); rep(i,Q){ ini(a , b , c); int ab = hld.lca(a , b); int bc = hld.lca(b , c); int ca = hld.lca(c , a); ll ans = 0; ans += memo[a] + memo[b] - 2 * memo[ab]; ans += memo[c] + memo[b] - 2 * memo[bc]; ans += memo[a] + memo[c] - 2 * memo[ca]; out(ans / 2); } }