25分版本 覆蓋性質 A
點選檢視程式碼
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<ll, ll> pir;
const int N = 2e5 + 5;
const ll INF = 1e18;
int n, m, r;
std::vector<int> g[N];
int fa[N], sz[N], wc[N], dep[N], vistime;
void dfs1(int u, int f)// 獲得 子樹大小,深度,父節點,重兒子等資訊
{
fa[u] = f;
sz[u] = 1;
dep[u] = dep[f] + 1;
for(auto v : g[u])
{
if(v == f)continue;
dfs1(v, u);
sz[u] += sz[v];
if(sz[v] > sz[wc[u]])wc[u] = v;
}
}
int dfn[N], rdfn[N], top[N];
void dfs2(int u, int Top)// 獲取 dfs 序當前重鏈的鏈頭
{
dfn[u] = ++vistime;
rdfn[vistime] = u;
top[u] = Top;
if(wc[u] != 0)
{
dfs2(wc[u], Top);
for(auto v : g[u])
{
if(v == fa[u] || v == wc[u])continue;
dfs2(v, v);
}
}
}
ll a[N], b[N], c[N], val[N];
struct Info
{
ll sum;
};
Info operator + (Info a, Info b)
{
Info rt;
rt.sum = max(a.sum, b.sum);
return rt;
}
struct Seg_Tree
{
Info tree[N << 2];
ll tag[N << 2];
void build(int l, int r, int rt)
{
if(l == r)
{
int id = rdfn[l];
tree[rt].sum = a[id] / b[id] + dep[id];
return;
}
int mid = l + r >> 1;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}
void pushdown(int l, int r, int rt)
{
if(tag[rt])
{
tag[rt << 1] += tag[rt];
tree[rt << 1].sum += tag[rt];
tag[rt << 1 | 1] += tag[rt];
tree[rt << 1 | 1].sum += tag[rt];
tag[rt] = 0;
}
}
void update(int l, int r, int rt, int L, int R, ll val)
{
if(L <= l && r <= R)
{
tree[rt].sum += val;
tag[rt] += val;
return;
}
pushdown(l, r, rt);
int mid = l + r >> 1;
if(L <= mid)update(l, mid, rt << 1, L, R, val);
if(R > mid) update(mid + 1, r, rt << 1 | 1, L, R, val);
tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}
Info query(int l, int r, int rt, int L, int R)
{
if(L <= l && r <= R)
{
return tree[rt];
}
pushdown(l, r, rt);
int mid = l + r >> 1;
if(R <= mid)return query(l, mid, rt << 1, L, R);
else if(L > mid)return query(mid + 1, r, rt << 1 | 1, L, R);
return query(l, mid, rt << 1, L, R) + query(mid + 1, r, rt << 1 | 1, L, R);
}
} T;
// 理論上每一個子樹只會查詢一次
// set<pir>sa;
// 可是又不是不變的
// 一共走了 x 步,我這條路走了 y 部
// 相當於
void solve()
{
int n;
cin >> n;
for(int i = 1; i <= n; i++)cin >> a[i] >> b[i] >> c[i];
for(int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
if(count(c + 1, c + n + 1, 0) != n)assert(1);
dfs1(1, 0);
dfs2(1, 0);
T.build(1, n, 1);
std::vector<ll> ans(n + 1);
set<pir>sa;
T.update(1, n, 1, dfn[1], dfn[1], -INF);
for(int x : g[1])
{
ll val = T.query(1, n, 1, dfn[x], dfn[x] + sz[x] - 1).sum;
sa.insert({val, x});
}
int now = 0;
while(!sa.empty())
{
auto it = sa.end();
--it;
auto [val, u] = *it;
ans[u] = ++now;
sa.erase(it);
T.update(1, n, 1, dfn[u], dfn[u] + sz[u] - 1, -1);
for(auto x : g[u])
{
if(x == fa[u])continue;
ll val = T.query(1, n, 1, dfn[x], dfn[x] + sz[x] - 1).sum;
sa.insert({val, x});
}
T.update(1, n, 1, dfn[u], dfn[u], -INF);
}
ll maxx = 0;
for(int i = 1; i <= n; i++)
{
ll tot = ans[i] + a[i] / b[i];
if(a[i] % b[i])tot++;
maxx = max(maxx, tot);
}
cout << maxx << '\n';
}
int main()
{
// freopen("P4779_1.in", "r", stdin);
//freopen("pi.txt","w",stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
solve();
}
/*
找一個dfs序使得 dfn[i] +a[i] 的最大值最小
可以發現 dfs 序最後結束的位置一定是葉子節點
這玩意一定是個貪心 + 資料結構之類的
考慮向哪條路走會更優?
肯定考慮向當前最大的地方走啊,然後剩下的所有沒到的點 time+1
*/