树上差分
树上差分
Global Positioning System
题意:
通过m次测量,测量出n个点之间的大小关系,(三维的大小关系),然后有可能一个点测量出错了,问你有哪些边修改之后能够保证这些点之间的大小关系相同。
如果 1 -> 2 (1,1,1), 1->3(2,2,2) 如果2->3 不等于 1,1,1 那么这三个点中有一个是错的,急需要修改。
所以结果需要输出
3
1 2 3
思路:
如何判断一个点是不是树边
如果一个点走到了一个走过同时deep比自己小的点,那么这个点一定是一个非树边。
非矛盾
然后如果这个点的两次 相对距离如果相等的话,那么这个环就不能动,因为他已经是一个没有矛盾的环了,你动任意一条边都会让这个环的矛盾。如果当前点 是 u ,deep 比较小的那个点是 j ,相当于把
矛盾
然后如果这个点的两次 相对距离如果不相等的话,那么这个环就必须要动,因如果当前点 是 u ,deep 比较小的那个点是 j ,相当于把
快速处理一条链上问题
用树上差分 将deep小的-1,把deep 大的那个点+1,然后递归回去的时候累加,就可以达到将一条链中的除了开始的那个点以外其他的点加1的操作。
Code
#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#define int long long
#define x first
#define y second
#define ps push_back
#define endl '\n'
#define kd \
ios::sync_with_stdio(false); \
cin.tie(); \
cout.tie(0);
using namespace std;
typedef pair<int, int> pi;
const int N = 2e6 + 100, mod = 1e9 + 7, INF = 1e10;
int lowbit(int x) { return x & -x; }
int gcd(int a, int b) { return a % b == 0 ? b : gcd(b, a % b); }
struct T {
int v, a, b, c, id;
// T to_pair()
T operator+(const T x) const {
return {v, a + x.a, b + x.b, c + x.c, id + x.id};
// 说一下为啥v和id 也要+
/*
可以不加但是必须要有人把这个坑填上,因为返回的是一个结构体,如果你前面没有填上的话。那么他会默认把你的 前面几个看成0 也就是 如果你当前是 {1,a1,b1,c1,id1}+{2,a2,b2,c3,id2}
如果你写成 {a + x.a, b + x.b, c + x.c, id + x.id}; 那么新的v3=a+x.a a3=b+x.b, b3=c1+x.c c3=0;
*/
}
bool operator==(const T x) const { return a == x.a && b == x.b && c == x.c; }
};
vector<T> v[N];
int de[N], st[N], s1[N], s2[N];
// de 代表深度,用来判断是不是非树边,st 看有没有走过
// s1 s2 分别代表两种类型的权值,矛盾和非矛盾
/*
最后的结果,肯定是一个一定能动的点也就是 =没有被锁定过也就是 s1=0的点。
同时要s2最大,代表修改这个点能够将所有的有矛盾的环改对,
*/
T cur[N];// 每个点第一次的相对距离
int ex;
/*
ex 的作用比较抽象,建议画个图: 如果一个环上每个点都能被修改,同时 s2的最大值是1,说明有矛盾的环只有一个,那么我们如果直接枚举 s1=0 s2=1的情况会漏掉 那个非树边,所以我们用ex记录非树边,特别判断一下这个情况。
*/
int id[N];
//每个点对应的边的编号
// 当前点、 当前的权值 、 边的编号、上一个点
void dfs(int u, T nw, int fid, int pr) {
de[u] = de[pr] + 1;//...
cur[u] = nw;//u的第一次出现的权值就是
id[u] = fid;// 点对应的边的编号
st[u] = 1;// 出现过
for (int i = 0; i < v[u].size(); i++) {
int j = v[u][i].v, id = v[u][i].id;
if (j == pr) continue;
// cout << j << " " << u << endl;
if (!st[j]) {//如果没出现过先递归下去
T nw2 = nw + v[u][i];// 相对距离要加
dfs(j, nw2, id, u);//递归下去
//累加
s1[u] += s1[j];
s2[u] += s2[j];
} else if (de[j] < de[u]) {
T nw2 = nw + v[u][i], nw3 = cur[j];
if (nw2 == nw3) {//对应两种情况
s1[j]--;
s1[u]++;
} else {
s2[j]--;
s2[u]++;
ex = v[u][i].id;// 可能会用到特判 只有mx只有1的情况
}
}
}
}
void solve() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int a, b;
int x, y, z;
cin >> a >> b >> x >> y >> z;
v[a].ps({b, x, y, z, i});
v[b].ps({a, -x, -y, -z, i});
}
T cc = {0, 0, 0, 0, 0};
dfs(1, cc, 0, 0);
int mx = 0;
for (int i = 1; i <= n; i++) mx = max(mx, s2[i]);//最后取出需要修改环的最大值
vector<int> ans;
for (int i = 2; i <= n; i++) {
if (s2[i] == mx && !s1[i]) {// 上面说过了!
//只有一个点 能够满足最大的矛盾环数同时他自己 是能动的情况下。
ans.ps(id[i]);
}
}
if (mx == 1) {//也说过了
ans.ps(ex);
}
cout << ans.size() << endl;
sort(ans.begin(), ans.end());
for (int i = 0; i < ans.size(); i++) {
cout << ans[i] << " ";
}
cout << endl;
}
signed main() {
kd;
int _;
_ = 1;
// cin>>_;
while (_--) solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】