CodeForces - 464E - The Classic Problem
题面
题解
写的头疼
先看题, 明显是最短路, 关键是如何保存距离, 毕竟太大了
而且题目给的就是二进制, 就想到了要拆位,
一般的拆位用个可持续化 01trie 就好了, 这道题真的毒瘤还是 2进制下1e5 的范围
那只能可持续化权值线段树了, 好能存路径了, 但问题来了, 毕竟是二进制要考虑进位的
进位
先说为什么不能暴力进位, 看着组数据
100000 99999
1 2 0
2 3 1
...
49999 50000 49998
50000 50001 0
50000 50002 0
...
50000 100000 0
1 100000
在点50000之后就是 二进制下整个暴力进位, 每次相当于重新建一棵权值线段树,
时间复杂度就成了 n * n * logn(n = 5e4)
50001~100000之间建的线段树空间复杂度为 n * n * logn(n = 5e4)
时空全爆, 只不过是老题, 没人提供这组hack数据罢了
什么是二进制进位?
就是有一段连续的 11111, 你又在其中 + 1, 就进位了
说白了, 就是清空一段, 在让一个位置 + 1
而时空复杂度都是 2 * logn = logn
想象一颗满的权值线段树
左孩子要进位, 就清空, 复杂度 logn,
然后让 右孩子 + 1, 如果右孩子的左孩子要进位, 又是一条log, 但是是 log(n/2)的
不断深入, 最后复杂度应为 logn + log(n/2) + log(n/4) ... < 2logn
对于右孩子还进位的返回不就完事了, 那是该当前这颗树的祖先的右孩子改进位了
比较大小
这里不能直接去比, 看着想 logn 其实不然,
int cmp(int x, int y, int l, int r) {
if (tr[x].val == 0 && tr[y].val == 0) return 0;
else if (tr[x].val == 0) return -1;
else if (tr[y].val == 0) return 1;
if (l == r) return tr[x].val < tr[y].val ? -1 : tr[x].val > tr[y].val;
int mid = l + r >> 1, ans = cmp(tr[x].r, tr[y].r, mid + 1, r);
return ans ? ans : cmp(tr[x].l, tr[y].l, l, mid);
}
这就是不断的再找最远的右孩子知道出现某个二进制位置不一样, 复杂明显成线性了
那咋办, hash 啊, 我怕出错用了两次has, 不过选的好一次就行
但是随着acm的发展, 正规比赛时卡单has的, 一般双/三哈希卡不住, 这道题数据水了倒是单has能过
代码
struct CHT {
static const int N = 1e5 + 20, mod = 1e9 + 7, mod1 = 13331;
struct Node { int l, r, val, has[2]; } tr[N * 40];
int rt[N], tot, s[2][N] = { {1}, {1} };
void init() {
rep(i, 1, 1e5 + 16)
s[0][i] = ((ll)s[0][i - 1] << 1) % mod,
s[1][i] = ((ll)s[1][i - 1] << 1) % mod1;
}
void clear(int& x, int y, int l, int r, int L, int R) {
if (L <= l && r <= R) { x = 0; return; }
tr[x = ++tot] = tr[y];
int mid = l + r >> 1;
if (mid >= L) clear(tr[x].l, tr[y].l, l, mid, L, R);
if (mid < R) clear(tr[x].r, tr[y].r, mid + 1, r, L, R);
tr[x].val = tr[tr[x].l].val + tr[tr[x].r].val;
tr[x].has[0] = ((ll)tr[tr[x].l].has[0] + tr[tr[x].r].has[0]) % mod;
tr[x].has[1] = ((ll)tr[tr[x].l].has[1] + tr[tr[x].r].has[1]) % mod1;
}
bool update(int& x, int y, int l, int r, int val) {
if (tr[y].val == r - l + 1) {
clear(x, y, l, r, val, r);
return 1;
}
tr[x = ++tot] = tr[y];
if (l == r) return tr[x].val = 1, tr[x].has[0] = s[0][l], tr[x].has[1] = s[1][l], 0;
int mid = l + r >> 1, f = 0;
if (mid >= val) {
if (update(tr[x].l, tr[y].l, l, mid, val))
f = update(tr[x].r, tr[y].r, mid + 1, r, mid + 1);
}
else f = update(tr[x].r, tr[y].r, mid + 1, r, val);
tr[x].has[0] = ((ll)tr[tr[x].l].has[0] + tr[tr[x].r].has[0]) % mod;
tr[x].has[1] = ((ll)tr[tr[x].l].has[1] + tr[tr[x].r].has[1]) % mod1;
return tr[x].val = tr[tr[x].l].val + tr[tr[x].r].val, f;
}
int cmp(int x, int y) {
if (tr[x].val == 0 && tr[y].val == 0) return 0;
else if (tr[x].val == 0) return -1;
else if (tr[y].val == 0) return 1;
if (tr[tr[x].r].has[0] != tr[tr[y].r].has[0] || tr[tr[x].r].has[1] != tr[tr[y].r].has[1])
return cmp(tr[x].r, tr[y].r);
return cmp(tr[x].l, tr[y].l);
}
} T;
const int N = 1e5 + 16;
int n, m, _, k;
struct Node { int x, rt; bool operator <(Node a) const { return T.cmp(rt, a.rt) == 1; } };
int main() {
IOS; cin >> n >> m; vector<vector<PII>> h(n + 1);
rep(i, 1, m) {
int u, v, c; cin >> u >> v >> c;
h[u].pb({ v, c }); h[v].pb({ u, c });
}
int s, t; cin >> s >> t; T.init();
priority_queue<Node> q; q.push({ s, 0 });
vector<bool> v(n + 1); VI pre(n + 1);
while (!q.empty()) {
int x = q.top().x, tmp; q.pop();
if (v[x]) continue; v[x] = 1;
if (x == t) break;
for (auto& e : h[x]) {
if (v[e.fi]) continue;
T.update(tmp, T.rt[x], 0, N, e.se);
if (!T.rt[e.fi] || T.cmp(T.rt[e.fi], tmp) == 1)
q.push({ e.fi, T.rt[e.fi] = tmp }), pre[e.fi] = x;
}
}
if (!v[t]) { cout << -1; return 0; } T.init();
VI ans; for (int i = t; i; ans.pb(i), i = pre[i]);
cout << T.tr[T.rt[t]].has[0] << '\n' << ans.size() << '\n';
per(i, ans.size() - 1, 0) cout << ans[i] << ' ';
return 0;
}