洛谷P4556思路分享(线段树合并,树上差分,线段树上二分)
https://www.luogu.com.cn/problem/P4556
题意概述
有 \(n\) 座房屋,构成一棵树。救济粮分 \(m\) 次发放,每次给 \(x\) 到 \(y\) 路径上每个房屋发放一袋 \(z\) 类型的救济粮。问所有救济粮发放完之后,每座房屋数量最多的救济粮类型,如果没有,输出 \(0\) 。
\(1 \leq n, m \leq 10^5\),\(1 \leq z \leq 10^5\)。
思路
给 \(x\) 到 \(y\) 路径上所有节点分发同样的救济粮,考虑树上差分。
救济粮信息可以用值域线段树维护,最后统计答案的时候合并线段树即可。
具体的,动态开点,给每个节点建一棵值域线段树,维护该节点所有类型的救济粮中数量最大值,查询这个最大值对应的类型只需要在线段树上二分即可。
发放救济粮的过程,记 \(x\) 和 \(y\) 的 \(LCA\) 为 \(t\) ,\(x\) 和 \(y\) 节点的 \(z\) 类型救济粮数量 \(+1\),\(t\) 和 \(t\) 的父节点的 \(z\) 类型救济粮数量 \(-1\) 。
最后向上合并线段树,计算每个节点答案即可,代码中使用了倍增求 \(LCA\) 。
时间复杂度 \(\mathcal{O}((n+m) \log N)\),\(N\) 是 \(z\) 的上界。
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5+5;
const int INF = 1e9;
class node{
public:
int left=-1,right=-1,mx=0;
};
void solve(){
int n,m;
cin >> n >> m;
vector<vector<int>> adj(n+1);
for (int i=0;i<n-1;i++){
int u,v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
vector<int> depth(n+1),par(n+1,-1);
function<void(int)> dfs = [&](int u){
for (auto& v:adj[u]){
if (v==par[u]) continue;
depth[v] = depth[u]+1;
par[v] = u;
dfs(v);
}
};
dfs(1);
vector<vector<int>> dp(n+1,vector<int>(32,-1));
for (int i=1;i<=n;i++){
dp[i][0] = par[i];
}
for (int k=1;k<32;k++){
for (int i=1;i<=n;i++){
if (dp[i][k-1]==-1) continue;
dp[i][k] = dp[dp[i][k-1]][k-1];
}
}
auto getlca = [&](int u,int v){
if (depth[u]<depth[v]){
swap(u,v);
}
int diff = depth[u]-depth[v];
for (int k=31;k>=0;k--){
if (diff>>k&1){
u = dp[u][k];
}
}
if (u==v) return u;
for (int k=31;k>=0;k--){
if (dp[u][k]!=-1 && dp[v][k]!=-1 && dp[u][k]!=dp[v][k]){
u = dp[u][k];
v = dp[v][k];
}
}
return par[u];
};
vector<node> seg{{}};
vector<int> root(n+1);
for (int i=1;i<=n;i++){
seg.push_back({});
root[i] = seg.size()-1;
}
function<void(int,int,int,int,int)> update = [&](int rt,int l,int r,int pos,int val){
if (l==r){
seg[rt].mx += val;
return;
}
int mid = l+r >> 1;
if (pos<=mid){
if (seg[rt].left==-1){
seg.push_back({});
seg[rt].left = seg.size()-1;
}
update(seg[rt].left,l,mid,pos,val);
}
else{
if (seg[rt].right==-1){
seg.push_back({});
seg[rt].right = seg.size()-1;
}
update(seg[rt].right,mid+1,r,pos,val);
}
seg[rt].mx = max(seg[rt].left!=-1?seg[seg[rt].left].mx:-INF,seg[rt].right!=-1?seg[seg[rt].right].mx:-INF);
};
while (m--){
int x,y,z;
cin >> x >> y >> z;
int t = getlca(x,y);
update(root[x],1,N,z,1);
update(root[y],1,N,z,1);
update(root[t],1,N,z,-1);
if (par[t]!=-1) update(root[par[t]],1,N,z,-1);
}
function<int(int,int,int,int)> merge = [&](int rt1,int rt2,int l,int r){
if (rt1==-1 || rt2==-1){
return rt1==-1?rt2:rt1;
}
if (l==r){
seg[rt1].mx = seg[rt1].mx+seg[rt2].mx;
return rt1;
}
int mid = l+r >> 1;
seg[rt1].left = merge(seg[rt1].left,seg[rt2].left,l,mid);
seg[rt1].right = merge(seg[rt1].right,seg[rt2].right,mid+1,r);
seg[rt1].mx = max(seg[rt1].left!=-1?seg[seg[rt1].left].mx:-INF,seg[rt1].right!=-1?seg[seg[rt1].right].mx:-INF);
if (seg[rt1].mx==-INF){
seg[rt1].mx = 0;
}
return rt1;
};
function<int(int,int,int,int)> first_valid = [&](int rt,int l,int r,int mx){
if (rt==-1 || seg[rt].mx<mx){
return -1;
}
if (l==r){
return l;
}
int mid = l+r >> 1;
int res = first_valid(seg[rt].left,l,mid,mx);
if (res!=-1){
return res;
}
return first_valid(seg[rt].right,mid+1,r,mx);
};
vector<int> res(n+1);
function<void(int)> dfs2 = [&](int u){
for (auto& v:adj[u]){
if (v==par[u]) continue;
dfs2(v);
merge(root[u],root[v],1,N);
}
res[u] = seg[root[u]].mx==0?0:first_valid(root[u],1,N,seg[root[u]].mx);
};
dfs2(1);
for (int i=1;i<=n;i++){
cout << res[i] << '\n';
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
// cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号