P3066 [USACO12DEC]Running Away From the Barn G 树上差分+倍增/区间数点
//题意:n个节点的树,每条边有边权,现在要求对于每一个结点u的子树中有多少个节点 // 满足到u点的距离不大于t // 思路:树上差分思想还是比较好想到的,因为要满足dep[x]-dep[u]<=t,所以在x之上到 // u之间的所有点对u点的答案都有贡献,所以上差分就好 // // 主要不是很熟练的的是如何对于每个询问求满足条件的x // 1.第一种实现方法利用倍增,从节点u开始向下倍增(注意这里犯了一个很严重的错误, // 我们以节点u为头向下倍增是不可行的,因为他会有多个儿子,也就是分叉。正确的 // 方法是对每一个x向上倍增,找满足条件的祖先节点) // // 这样可以在logn步的规模内 // 跳到x节点。因为我们需要在每个节点都进行一次倍增查询,所以总的复杂度是 // O(nlogn)。 // //思路一 #include<bits/stdc++.h> using namespace std; #define int long long const int N = 2e5 + 10; struct edge { int len, to; edge(int a = 0, int b = 0) { len = a, to = b; } }; vector<edge> mp[N]; int n, t; int lis[N][20], dep[N], ans[N]; void dfs(int x) { for (auto y : mp[x]) { if (y.to == lis[x][0]) continue; dep[y.to] = dep[x] + y.len; dfs(y.to); } } void dfs2(int y) {//当前遍历节点 int x = y; for (int i = 18; i >= 0; i--) { if (lis[x][i] == 0) continue; if (dep[lis[x][i]] + t < dep[y]) continue; x = lis[x][i]; } ans[y] += 1; ans[lis[x][0]] -= 1; for (auto w : mp[y]) { if (w.to == lis[y][0]) continue; dfs2(w.to); ans[y] += ans[w.to]; } } signed main() { cin >> n >> t; for (int i = 2; i <= n; i++) { int a, b; cin >> a >> b; mp[i].push_back({ b,a }); mp[a].push_back({ b,i }); lis[i][0] = a; } for (int i = 1; i <= 18; i++) { for (int j = 1; j <= n; j++) { lis[j][i] = lis[lis[j][i - 1]][i - 1]; } } lis[1][0] = 0; dfs(1); dep[1] = 0; dfs2(1); for (int i = 1; i <= n; i++) cout << ans[i] << endl; return 0; } /* 4 5 1 4 2 3 1 5 */ //思路:类似于区间数点问题,就是先dfs求出每个点的dep,然后按dep降序排序,删除和插入就行,这里代码中详细讲,这个思路有点妙啊 //思路二 #include<bits/stdc++.h> using namespace std; const int N = 200005; int n, id[N], r[N], now, ans[N]; pair<long long, int> a[N]; struct BIT { int tree[N]; void modify(int x, int val) { for (; x <= n; x += x & -x) tree[x] += val; } int query(int x) { int ans = 0; for (; x; x -= x & -x) ans += tree[x]; return ans; } }T; //树状数组模板 vector<pair<int, long long>> mat[N]; void dfs(int k) { id[k] = ++now; //dfs序 for (auto e : mat[k]) { a[e.first] = make_pair(a[k].first + e.second, e.first); dfs(e.first); } r[k] = now; //结束时间戳 } int main() { long long l; cin >> n >> l; for (int i = 2; i <= n; i++) { int p; long long w; cin >> p >> w; mat[p].push_back(make_pair(i, w)); } a[1] = make_pair(0ll, 1); dfs(1); sort(a + 1, a + n + 1); int j = n;//j初始值为n,也就是最深 for (int i = n; i; i--) { for (; a[j].first - a[i].first > l; j--) T.modify(id[a[j].second], -1); //删除超过距离超过L的点 T.modify(id[a[i].second], 1); //插入当前点 ans[a[i].second] = T.query(r[a[i].second]) - T.query(id[a[i].second] - 1); //统计子树答案 } //这里删点的逻辑很妙,因为我们按照dep的降序的话,i和j有两种情况 // 情况一:i和j是在同一条树链上的,那么dep[j]-dep[i]>l的话,j点是肯定要删除的 // 情况二:i和j没在同一条树链上,这个时候dep[j]-dep[i]>l我们也把j删除了,因为既然对于i来说j已经不满足条件了,那么 // 对于j这条树链上的其他点,j也无法做出贡献了(因为dep[其他点]<=dep[i]) // for (int i = 1; i <= n; i++) cout << ans[i] << endl; return 0; }
分类:
2023年1月
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!