結果
| 問題 |
No.399 動的な領主
|
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2023-12-25 19:57:29 |
| 言語 | C++17(gcc12) (gcc 12.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 510 ms / 2,000 ms |
| コード長 | 5,988 bytes |
| コンパイル時間 | 29,139 ms |
| コンパイル使用メモリ | 359,040 KB |
| 最終ジャッジ日時 | 2025-02-18 14:36:21 |
|
ジャッジサーバーID (参考情報) |
judge1 / judge3 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| other | AC * 19 |
ソースコード
#pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include<bits/stdc++.h>
#include<atcoder/all>
using namespace std;
using namespace atcoder;
using ll=long long;
using P=pair<ll,ll>;
using T=tuple<ll,ll,ll>;
struct S{
long long value;
int size;
};
using F = long long;
S op(S a, S b){ return {a.value+b.value, a.size+b.size}; }
S e(){ return {0, 0}; }
S mapping(F f, S x){ return {x.value + f*x.size, x.size}; }
F composition(F f, F g){ return f+g; }
F id(){ return 0; }
void IO(){
ios::sync_with_stdio(false);
std::cin.tie(nullptr);
}
struct HLD{
//References:
// https://judge.yosupo.jp/submission/72507 原典
// https://atcoder.jp/contests/abc294/submissions/39859050 原典より使いやすいsegtree対応ver
// https://atcoder.jp/contests/past202010-open/submissions/33495220 原典より使いやすいlazy_segtree対応ver
// ↑がReferencesの中で最もおすすめ
//頂点に初期値を与える場合の注意点
// -hld.build()の実行後に行う
// -頂点uはHLD内部ではhld.in[u]となる
//辺に値がある場合は子側の頂点に持たせてedge=trueで関数を呼び出す
//lazy_segtreeを使用
ll N,root;
vector<vector<ll>> G;
vector<ll> parent;//頂点uの親
vector<ll> depth;//頂点uの深さ
vector<ll> sz;//頂点uを根とする部分木のサイズ(heavy)
vector<ll> in;//HLD時の探索順序(Euler Tour)
vector<ll> out;//後述
vector<ll> head;//頂点uが属するHL分解後の連結成分の根
vector<ll> rev;//探索順序番号から元の頂点番号への逆引き
ll t=0;//探索順序の計算用
//[in[u],out[u]) :=頂点uを根とする部分木に対応
//[in[head[u]],in[u]] :=HLD後の連結成分における頂点uからそのheadまでのパスに対応
HLD(){}
HLD(ll n,ll r=0):N(n),root(r){
G.resize(N); parent.resize(N); depth.resize(N); sz.resize(N);
in.resize(N); out.resize(N); head.resize(N); rev.resize(N);
}
//双方向に辺を張る
void add_edge(ll u,ll v){
assert(0<=u&&u<N);
assert(0<=v&&v<N);
G[u].push_back(v);
G[v].push_back(u);
}
//各部分木のサイズを求める
void dfs_sz(ll u,ll p,ll d){
parent[u]=p;
depth[u]=d;
sz[u]=1;
//heavy edgeが先頭に来るように維持しながら探索する
if(G[u].size()&&G[u][0]==p){
swap(G[u][0],G[u].back());
}
for(ll &v:G[u]){
//NOTE: swapのためにポインタを用いる必要がある
if(v!=p){
dfs_sz(v,u,d+1);
sz[u]+=sz[v];
if(sz[G[u][0]]<sz[v]){
swap(G[u][0],v);
}
}
}
}
//HLD(Heavy Light Decomposition)
void dfs_hld(ll u,ll p){
in[u]=t++;
rev[in[u]]=u;
for(const ll &v:G[u]){
if(v!=p){
head[v]=(v==G[u][0]?head[u]:v);// heavy or light
dfs_hld(v,u);
}
}
out[u]=t;
}
void build(){
dfs_sz(root,-1,0);
dfs_hld(root,-1);
}
//頂点uから深さdだけ親をたどる (level-ancestor) : O(log N)
ll la(ll u,ll d)const{
assert(0<=u&&u<N);
//辿った先が木上になければ-1を返す
if(depth[u]-d<0){
return -1;
}
while(1){
ll v=head[u];
if(in[u]-d>=in[v]){
return rev[in[u]-d];
}
d-=in[u]-in[v]+1;
u=parent[v];
}
}
//頂点u,vのLCA : O(log N)
ll lca(ll u,ll v)const{
assert(0<=u&&u<N);
assert(0<=v&&v<N);
while(1){
if(in[u]>in[v]){
swap(u,v);//in[u]<=in[v] (uが根側)
}
if(head[u]==head[v]){
return u;
}
v=parent[head[v]];
}
}
//パス間の辺数
ll distance(ll u,ll v)const{
assert(0<=u&&u<N);
assert(0<=v&&v<N);
return depth[u]+depth[v]-2*depth[lca(u,v)];
}
//頂点wがパス(u,v)上に存在するか : O(log N)
bool on_path(ll u,ll v,ll w)const{
assert(0<=u&&u<N);
assert(0<=v&&v<N);
assert(0<=w&&w<N);
return distance(u,w)+distance(w,v)==distance(u,v);
}
//パス[u,v]に対する何らかのクエリ : O((log N)^2)
//閉区間に対応するクエリであることに注意
vector<P> query(ll u,ll v,bool edge){
assert(0<=u&&u<N);
assert(0<=v&&v<N);
vector<P> res;
while(1){
if(in[u]>in[v]){
swap(u,v);
}
if(head[u]==head[v]){
break;
}
res.emplace_back(in[head[v]],in[v]);
v=parent[head[v]];
}
res.emplace_back(in[u]+edge,in[v]);
return res;
}
//頂点uを根とする部分木に対する更新クエリ : O(log N)
void apply_subtree(ll u,bool edge,function<void(ll,ll)> func){
assert(0<=u&&u<N);
func(in[u]+edge,out[u]-1);
}
//頂点uを根とする部分木に対する取得クエリ : O(log N)
template<class S>
S prod_subtree(ll u,bool edge,function<S(ll,ll)> func){
assert(0<=u&&u<N);
return func(in[u]+edge,out[u]-1);
}
//頂点uを返す
//頂点uはHLD内部ではhld.in[u]となる
ll vertex_idx(ll u){
return in[u];
}
//辺に値がある場合は子側の頂点に持たせてedge=trueで関数を呼び出す
ll edge_idx(ll u,ll v){
return max(in[u],in[v]);
}
};
int main(){
IO();
ll n;
cin>>n;
vector<P> uv;
vector<T> edges;
HLD hld(n);
for(ll i=0;i<n-1;i++){
ll u,v;
cin>>u>>v;
u--;
v--;
uv.emplace_back(u,v);
hld.add_edge(u,v);
edges.emplace_back(u,v,0);
}
hld.build();
lazy_segtree<S,op,e,F,mapping,composition,id> seg(vector<S>(n,{0,1}));
for(ll i=0;i<n;i++){
seg.apply(i,i+1,0);
}
ll q;
cin>>q;
bool edge=false;
while(q--){
ll a,b;
cin>>a>>b;
a--;
b--;
vector<P> add_tax=hld.query(a,b,edge);
for(auto pairs:add_tax){
ll l=pairs.first;
ll r=pairs.second;
seg.apply(l,r+1,1);
}
}
ll ans=0;
for(ll i=0;i<n;i++){
ll val=seg.prod(i,i+1).value;
ans+=val*(val+1)/2;
}
cout<<ans<<endl;
}