2026.5.2情报系统听课笔记

树状数组区间加区间和。
先把原序列差分以进行区间加。
区间和:
所以只需要维护 \(\sum c_i\) 和 \(\sum (i\cdot c_i)\) 即可。
点击查看代码
#include <iostream>
#define lowbit(x) x & (-x)
using std::cin;
using std::cout;
const int N = 2e5 + 10;
typedef long long ll;
int n;
ll a[N];
ll c[N];
ll sum1[N];
ll sum2[N];
void add1(int x, ll k)
{
for (int i = x; i <= n; i += lowbit(i))
sum1[i] += k;
}
void add2(int x, ll k)
{
for (int i = x; i <= n; i += lowbit(i))
sum2[i] += k;
}
void modify(int l, int r, ll k)
{
add1(l, k), add2(l, 1ll * l * k);
if (r < n)
add1(r + 1, -k), add2(r + 1, -1ll * (r + 1) * k);
}
ll ask1(int x)
{
ll ret = 0;
for (int i = x; i; i -= lowbit(i))
ret += sum1[i];
return ret * (x + 1);
}
ll ask2(int x)
{
ll ret = 0;
for (int i = x; i; i -= lowbit(i))
ret += sum2[i];
return ret;
}
ll query(int l, int r)
{
if (l == 1)
return ask1(r) - ask2(r);
return ask1(r) - ask1(l - 1) - (ask2(r) - ask2(l - 1));
}
int main()
{
cin >> n;
int m;
cin >> m;
for (int i = 1; i <= n; ++i)
cin >> a[i], c[i] = a[i] - a[i - 1], add1(i, c[i]), add2(i, 1ll * i * c[i]);
for (int i = 1; i <= m; ++i)
{
int opt;
cin >> opt;
if (opt == 1)
{
int x, y;
ll k;
cin >> x >> y >> k;
modify(x, y, k);
}
else if (opt == 2)
{
int x, y;
cin >> x >> y;
cout << query(x, y) << '\n';
}
}
return 0;
}
若 \(x\in [1, 2^k]\) 且 \(x\) 随机。
则树状数组区间求和的期望复杂度?
注意到我们每次 -=lowbit(x) 的次数是 \(\operatorname{popcount}(x)\) 的。
所以期望是
对于每个 \(i\),我们有一个 \(2^k - 1 - i\) 与其配对,使得一共有 \(k\) 个 \(1\)。
所以期望就是
对于单点加的复杂度,因为我们每次访问到最高节点后还要再 +=lowbit(x) 一遍,所以期望就是
因此,树状数组的复杂度是约 \(\dfrac{1}{2}\) 的,非常优秀了。

用线段树做:
你考虑对于每个数,它都会被加 \(\delta = K - lD\),其次,它会被加 \(e = iD\)。
所以说,你可以维护两个 tag,一个是维护 \(\delta\),另一个是维护 \(sumD\),左儿子就是 \(sum_l + len_l\cdot\delta + D\cdot\dfrac{len_l\cdot(l + mid)}{2}\),右儿子类似。
点击查看代码
#include <iostream>
using std::cin;
using std::cout;
const int N = 5e5 + 10;
typedef long long ll;
struct Node
{
ll tag1; // delta
ll tag2; // D
ll sum;
Node()
{
sum = tag1 = tag2 = 0;
}
friend Node operator+(const Node &l, const Node &r)
{
Node ret;
ret.sum = l.sum + r.sum;
return ret;
}
} z[N << 2];
ll a[N];
#define root 1, n, 1
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
void build(int l, int r, int rt)
{
if (l == r)
{
z[rt].sum = a[l];
return;
}
int mid = (l + r) >> 1;
build(lson);
build(rson);
z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
void push_down(int l, int r, int rt)
{
if (z[rt].tag1)
{
int mid = (l + r) >> 1;
z[rt << 1].tag1 += z[rt].tag1;
z[rt << 1 | 1].tag1 += z[rt].tag1;
z[rt << 1].sum += z[rt].tag1 * (mid - l + 1);
z[rt << 1 | 1].sum += z[rt].tag1 * (r - mid);
z[rt].tag1 = 0;
}
if (z[rt].tag2)
{
int mid = (l + r) >> 1;
z[rt << 1].tag2 += z[rt].tag2;
z[rt << 1 | 1].tag2 += z[rt].tag2;
z[rt << 1].sum += z[rt].tag2 * (mid + l) * (mid - l + 1) / 2;
z[rt << 1 | 1].sum += z[rt].tag2 * (r + mid + 1) * (r - mid - 1 + 1) / 2;
z[rt].tag2 = 0;
}
}
void modify(int l, int r, int rt, int nowl, int nowr, ll k, ll d)
{
if (nowl <= l && r <= nowr)
{
z[rt].tag1 += k - nowl * d;
z[rt].tag2 += d;
z[rt].sum += (k - nowl * d) * (r - l + 1) + d * (r - l + 1) * (r + l) / 2;
return;
}
int mid = (l + r) >> 1;
push_down(l, r, rt);
if (nowl <= mid)
modify(lson, nowl, nowr, k, d);
if (nowr > mid)
modify(rson, nowl, nowr, k, d);
z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
Node query(int l, int r, int rt, int nowl, int nowr)
{
if (nowl <= l && r <= nowr)
return z[rt];
int mid = (l + r) >> 1;
push_down(l, r, rt);
if (nowl <= mid)
{
if (nowr > mid)
return query(lson, nowl, nowr) + query(rson, nowl, nowr);
else
return query(lson, nowl, nowr);
}
else
return query(rson, nowl, nowr);
}
int main()
{
cin.tie(nullptr);
cout.tie(nullptr);
std::ios::sync_with_stdio(false);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i)
cin >> a[i];
build(root);
while (m--)
{
int opt;
cin >> opt;
if (opt == 1)
{
int l, r;
ll k, d;
cin >> l >> r >> k >> d;
modify(root, l, r, k, d);
}
else if (opt == 2)
{
int p;
cin >> p;
cout << query(root, p, p).sum << '\n';
}
else if (opt == 3)
{
int x, y;
cin >> x >> y;
cout << query(root, x, y).sum << '\n';
}
}
return 0;
}
用树状数组做。
你先维护一个 \(d_i = a_i - a_{i - 1}, dd_i = d_i - d_{i - 1}\)。
前缀和就是
我们考虑算贡献。
因为 \(i,j\in\left[k, n\right]\),所以每个 \(a_k\) 会被算 \(C_{n - k + 1}^{1} + C_{n - k + 1}^{2}\) 次(相等或不相等)。
考虑到 \(C_{n - k + 1}^{1} + C_{n - k + 1}^{2} = a\cdot dd_k\cdot k^2 + b\cdot dd_k\cdot k + c\cdot dd_k\),\(a,b,c\) 为定值(具体看代码)。
直接维护 \(dd_k\cdot k^2,dd_k\cdot k, dd_k\) 的和即可(对于 \(a, b, c\),用查询的参数获取即可)。
点击查看代码
#include <iostream>
#define lowbit(x) x & (-x)
using std::cin;
using std::cout;
const int N = 5e5 + 10;
typedef long long ll;
int n;
ll a[N];
ll c[N];
ll d[N];
ll sum1[N];
ll sum2[N];
ll sum3[N];
void add1(int x, ll k)
{
for (int i = x; i <= n; i += lowbit(i))
sum1[i] += k;
}
void add2(int x, ll k)
{
for (int i = x; i <= n; i += lowbit(i))
sum2[i] += k;
}
void add3(int x, ll k)
{
for (int i = x; i <= n; i += lowbit(i))
sum3[i] += k;
}
ll ask1(int x)
{
ll ret = 0;
for (int i = x; i; i -= lowbit(i))
ret += sum1[i];
return ret;
}
ll ask2(int x)
{
ll ret = 0;
for (int i = x; i; i -= lowbit(i))
ret += sum2[i];
return ret;
}
ll ask3(int x)
{
ll ret = 0;
for (int i = x; i; i -= lowbit(i))
ret += sum3[i];
return ret;
}
ll query1(int l, int r)
{
if (l == 1)
return ask2(r) * (-1) + ask1(r) * (r + 1);
return ask2(r) * (-1) - ask2(l - 1) * (-1) + ask1(r) * (r + 1) - ask1(l - 1) * (l - 1 + 1);
}
ll query2(int l, int r)
{
if (l == 1)
return ask3(r) + ask2(r) * (-(3 + 2 * r)) + ask1(r) * (1ll * r * r + 3 * r + 2);
return ask1(r) * (1ll * r * r + 3 * r + 2) - ask1(l - 1) * (1ll * (l - 1) * (l - 1) + 3 * (l - 1) + 2) + ask2(r) * (-(3 + 2 * r)) - ask2(l - 1) * (-(3 + 2 * (l - 1))) + ask3(r) - ask3(l - 1);
}
int main()
{
cin >> n;
int m;
cin >> m;
for (int i = 1; i <= n; ++i)
{
cin >> a[i];
c[i] = a[i] - a[i - 1];
d[i] = c[i] - c[i - 1];
add1(i, d[i]);
add2(i, d[i] * i);
add3(i, d[i] * i * i);
}
for (int i = 1; i <= m; ++i)
{
int opt;
cin >> opt;
if (opt == 1)
{
int l, r;
ll k, D;
cin >> l >> r >> k >> D;
add1(l, k), add2(l, k * l), add3(l, k * l * l);
if (l + 1 <= n)
add1(l + 1, -k), add2(l + 1, -k * (l + 1)), add3(l + 1, -k * (l + 1) * (l + 1));
if (l + 1 <= n)
add1(l + 1, D), add2(l + 1, D * (l + 1)), add3(l + 1, D * (l + 1) * (l + 1));
if (r + 1 <= n)
add1(r + 1, D * (-(r - l + 1))-k), add2(r + 1, (D * (-(r - l + 1))-k) * (r + 1)), add3(r + 1, (D * (-(r - l + 1))-k) * (r + 1) * (r + 1));
if (r + 2 <= n)
add1(r + 2, D * (r - l)+k), add2(r + 2, (D * (r - l)+k) * (r + 2)), add3(r + 2, (D * (r - l)+k) * (r + 2) * (r + 2));
}
else if (opt == 2)
{
int p;
cin >> p;
cout << query1(1, p) << '\n';
}
else if (opt == 3)
{
int x, y;
cin >> x >> y;
cout << query2(x, y) / 2 << '\n';
}
}
return 0;
}

我们注意到,\(a_i\) 分为取模有效和取模无效,且若有效,则 \(a_i\) 取模后不会超过 \(\dfrac{a_i}{2}\),因此一个数最多取模 \(log\) 次。
所以直接像区间开根号那样暴力操作就行。
点击查看代码
#include <iostream>
using std::cin;
using std::cout;
const int N = 1e5 + 10;
typedef long long ll;
struct Node
{
ll sum;
ll max;
friend Node operator+(const Node &l, const Node &r)
{
Node ret;
ret.sum = l.sum + r.sum;
ret.max = std::max(l.max, r.max);
return ret;
}
} z[N << 2];
int a[N];
#define root 1, n, 1
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
void build(int l, int r, int rt)
{
if (l == r)
{
z[rt].max = a[l];
z[rt].sum = a[l];
return;
}
int mid = (l + r) >> 1;
build(lson);
build(rson);
z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
void mod(int l, int r, int rt, int nowl, int nowr, int p)
{
if (z[rt].max < p)
return;
if (l == r)
{
z[rt].max %= p;
z[rt].sum %= p;
return;
}
int mid = (l + r) >> 1;
if (nowl <= mid)
mod(lson, nowl, nowr, p);
if (nowr > mid)
mod(rson, nowl, nowr, p);
z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
void modify(int l, int r, int rt, int p, int k)
{
if (l == r)
{
z[rt].sum = k;
z[rt].max = k;
return;
}
int mid = (l + r) >> 1;
if (p <= mid)
modify(lson, p, k);
else
modify(rson, p, k);
z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
Node query(int l, int r, int rt, int nowl, int nowr)
{
if (nowl <= l && r <= nowr)
return z[rt];
int mid = (l + r) >> 1;
if (nowl <= mid)
{
if (nowr > mid)
return query(lson, nowl, nowr) + query(rson, nowl, nowr);
else
return query(lson, nowl, nowr);
}
else
return query(rson, nowl, nowr);
}
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i)
cin >> a[i];
build(root);
while (m--)
{
int opt;
cin >> opt;
if (opt == 1)
{
int l, r;
cin >> l >> r;
cout << query(root, l, r).sum << '\n';
}
else if (opt == 2)
{
int l, r, x;
cin >> l >> r >> x;
mod(root, l, r, x);
}
else if (opt == 3)
{
int k, x;
cin >> k >> x;
modify(root, k, x);
}
}
return 0;
}

先差分 \(d_i = a_{i+1} - a_i\)。
我们先考虑查询(将一个 \(i\) 拔高 \(x\)),对于原序列中的每三个位置,我们有四种情况。
1.

此时答案是原序列的答案 \(+\) \(2(x - d_i)\)。
2.

此时答案是原序列的答案 \(+\) \(2(x - d_i)\)。
3.

此时答案是原序列的答案 \(+\) \(2x\)。
4.

当向上移 \(x\) 后高于两边的话,就变了 \(2x + 2d_{i - 1} - 2d_i\)。
否则大概率不优。
结合上面几种情况:

要注意特判几种特殊情况。
点击查看代码
#include <iostream>
#include <cmath>
using std::cin;
using std::cout;
const int N = 1e5 + 10;
typedef long long ll;
struct Node
{
ll ming;
friend Node operator+(const Node &l, const Node &r)
{
Node ret;
ret.ming = std::min(l.ming, r.ming);
return ret;
}
} z[N << 2];
ll a[N];
ll d[N];
#define root 2, n - 1, 1
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
void build(int l, int r, int rt)
{
if (l == r)
{
z[rt].ming = std::max(-d[l - 1], 0ll) + std::max(d[l], 0ll);
return;
}
int mid = (l + r) >> 1;
build(lson);
build(rson);
z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
void modify(int l, int r, int rt, int p, ll k)
{
if (l == r)
{
z[rt].ming = k;
return;
}
int mid = (l + r) >> 1;
if (p <= mid)
modify(lson, p, k);
else
modify(rson, p, k);
z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
Node query(int l, int r, int rt, int nowl, int nowr)
{
if (nowl <= l && r <= nowr)
return z[rt];
int mid = (l + r) >> 1;
if (nowl <= mid)
{
if (nowr > mid)
return query(lson, nowl, nowr) + query(rson, nowl, nowr);
else
return query(lson, nowl, nowr);
}
else
return query(rson, nowl, nowr);
}
signed main()
{
int n;
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
ll ans = 0;
for (int i = 1; i < n; ++i)
d[i] = a[i + 1] - a[i], ans += llabs(d[i]);
build(root);
int q;
cin >> q;
while (q--)
{
int opt, l, r;
ll x;
cin >> opt >> l >> r >> x;
if (opt == 2)
{
if (l - 1 >= 1)
{
ans -= llabs(d[l - 1]);
d[l - 1] += x;
ans += llabs(d[l - 1]);
modify(root, l, std::max(-d[l - 1], 0ll) + std::max(d[l], 0ll));
if (l - 2 >= 1)
modify(root, l - 1, std::max(-d[l - 2], 0ll) + std::max(d[l - 1], 0ll));
}
if (r < n)
{
ans -= llabs(d[r]);
d[r] -= x;
ans += llabs(d[r]);
if (r - 1 >= 1)
modify(root, r, std::max(-d[r - 1], 0ll) + std::max(d[r], 0ll));
if (r + 1 < n)
modify(root, r + 1, std::max(-d[r], 0ll) + std::max(d[r + 1], 0ll));
}
}
else if (opt == 1)
{
if (l == r)
{
ans -= llabs(d[l - 1]);
ans -= llabs(d[r]);
d[l - 1] += x;
d[r] -= x;
ans += llabs(d[l - 1]);
ans += llabs(d[r]);
cout << ans << '\n';
ans -= llabs(d[l - 1]);
ans -= llabs(d[r]);
d[l - 1] -= x;
d[r] += x;
ans += llabs(d[l - 1]);
ans += llabs(d[r]);
}
else
{
ll now;
Node now1 = query(root, (l > 1 ? l : 2), (r < n ? r : n - 1));
if (x < now1.ming)
now = ans;
else
now = ans + 2 * (x - now1.ming);
if (l == 1)
{
ans -= llabs(d[1]);
d[1] -= x;
ans += llabs(d[1]);
now = std::max(now, ans);
ans -= llabs(d[1]);
d[1] += x;
ans += llabs(d[1]);
}
if (r == n)
{
ans -= llabs(d[n - 1]);
d[n - 1] += x;
ans += llabs(d[n - 1]);
now = std::max(now, ans);
ans -= llabs(d[n - 1]);
d[n - 1] -= x;
ans += llabs(d[n - 1]);
}
cout << now << '\n';
}
}
}
return 0;
}

我们可以令 \(f_{i,j}\) 表示前 \(i\) 个分成 \(j\) 段的最小代价



注意到代码里加了滚动数组优化
点击查看代码
#include <iostream>
using std::cin;
using std::cout;
const int N = 3e4 + 5e3 + 10;
typedef long long ll;
const ll oo = 1e18;
struct Node
{
ll tag;
ll min;
Node()
{
tag = 0;
min = oo;
}
friend Node operator+(const Node &l, const Node &r)
{
Node ret;
ret.min = std::min(l.min, r.min);
return ret;
}
} z[N << 2];
ll f[N];
#define root 0, n, 1
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
void build(int l, int r, int rt)
{
if (l == r)
{
z[rt].min = f[l];
return;
}
int mid = (l + r) >> 1;
build(lson);
build(rson);
z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
void push_down(int rt)
{
if (z[rt].tag)
{
z[rt << 1].tag += z[rt].tag;
z[rt << 1].min += z[rt].tag;
z[rt << 1 | 1].tag += z[rt].tag;
z[rt << 1 | 1].min += z[rt].tag;
z[rt].tag = 0;
}
}
void modify(int l, int r, int rt, int nowl, int nowr, ll k)
{
if (nowl <= l && r <= nowr)
{
z[rt].min += k;
z[rt].tag += k;
return;
}
push_down(rt);
int mid = (l + r) >> 1;
if (nowl <= mid)
modify(lson, nowl, nowr, k);
if (nowr > mid)
modify(rson, nowl, nowr, k);
z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
Node query(int l, int r, int rt, int nowl, int nowr)
{
if (nowl <= l && r <= nowr)
return z[rt];
push_down(rt);
int mid = (l + r) >> 1;
if (nowl <= mid)
{
if (nowr > mid)
return query(lson, nowl, nowr) + query(rson, nowl, nowr);
else
return query(lson, nowl, nowr);
}
else
return query(rson, nowl, nowr);
}
int a[N];
int lst[N];
int vis[N];
int main()
{
f[0] = 0;
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; ++i)
f[i] = oo;
for (int i = 1; i <= n; ++i)
{
cin >> a[i];
lst[i] = vis[a[i]];
vis[a[i]] = i;
}
for (int i = 1; i <= k; ++i)
{
build(root);
for (int j = i; j <= n; ++j)
{
if (lst[j])
modify(root, 0, lst[j] - 1, j - lst[j]);
f[j] = query(root, i - 1, j - 1).min;
}
}
cout << f[n] << '\n';
return 0;
}





考虑到如果我们想模拟,就必须每一次操作都至少撞飞一只青蛙。
最先相撞的两个青蛙肯定是相邻的两个青蛙,所以我们看对于每两只青蛙,他们的位置、速度分别为 \(p_x, p_y, v_x, v_y\)(我们让 \(x\) 撞 \(y\)),他们的距离为 \(d = (py - px) \% m\):
- 若 \(v_x \geq d\),则可以撞上,所需时间为 \(1\)。
- 否则
- 若 \(v_x \leq v_y\),则 \(x\) 撞不上 \(y\)。
- 否则,能撞上,且时间为 \(\left\lceil \dfrac{d}{vx - vy} \right\rceil\)
我们每一次处理全局时间最小的事件,用链表维护每个青蛙的位置关系,用set或priority_queue维护最小时间即可。
注意,有可能一次不止撞飞一只青蛙,不过没什么区别,一个一个删除就行了
实在太难写了,贴一篇题解的代码吧。
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
const int inf = 1e9;
const ll INF = 1e15;
const int N = 1e5;
inline int read() {
int s = 0,f = 1;char ch = getchar();
while (!isdigit(ch)) f = ch == '-' ? -1 : 1, ch = getchar();
while (isdigit(ch)) s = (s << 3) + (s << 1) + ch - '0', ch = getchar();
return s*f;
}
int n,m,tot,vis[N + 10];
struct node {
int p,v,id;
}a[N + 10];
int cmp(node x,node y) {
return x.p < y.p;
}
int pre[N + 10],nxt[N + 10],ans[N + 10],tim[N + 10];
ll dd[N + 10];
int dis(int x,int y) {
int npx = a[x].p,npy = a[y].p;
return npx < npy ? npy - npx : m - npx + npy;
}
ll calc(int x,int y,int now) {
ll ndx = dd[x] + 1ll * a[x].v * (now - tim[x]),ndy = dd[y] + 1ll * a[y].v * (now - tim[y]);
if (a[x].id < a[y].id && dis(x,y) <= a[x].v) return 1;
else if (a[x].id < a[y].id && a[x].v > a[y].v) return ceil((dis(x,y) - ndx + ndy - a[x].v) * 1.0 / (a[x].v - a[y].v)) + 1;
return a[x].v <= a[y].v ? INF : ceil((dis(x,y) - ndx + ndy) * 1.0 / (a[x].v - a[y].v));
}
struct node2 {
int x,y,v,w;
bool operator < (const node2 &y) const {
return v == y.v ? a[x].id > a[y.x].id : v > y.v;
}
};
void del(int x) {
pre[nxt[x]] = pre[x];
nxt[pre[x]] = nxt[x];
pre[x] = nxt[x] = -1;
}
signed main() {
n = read();
m = read();
for (int i = 1;i <= n;i ++ ) a[i].p = read(), a[i].v = read(), a[i].id = i;
sort(a + 1,a + n + 1,cmp);
priority_queue<node2> q;
for (int i = 1;i <= n;i ++ ) nxt[i] = i % n + 1, pre[i] = (i + n - 2) % n + 1, q.push({i,nxt[i],calc(i,nxt[i],0),vis[nxt[i]] = ++tot});
while (q.size() > 1) {
node2 t = q.top();
if (pre[t.x] < 0 || t.y != nxt[t.x] || vis[t.y] != t.w) {
q.pop();
continue;
}
if (t.v >= INF) break;q.pop();
int x = t.x,lsp = a[x].p;
ll dx = 1ll * (t.v - tim[x]) * a[x].v;
dd[x] += dx;
tim[x] = t.v;
a[x].v --;
del(nxt[x]);
while (nxt[x] != x && dd[x] - (dd[nxt[x]] + 1ll * (t.v - tim[nxt[x]] - (a[x].id < a[nxt[x]].id)) * a[nxt[x]].v) >= dis(x,nxt[x])) a[x].v --, del(nxt[x]);
a[x].v = max(a[x].v,0ll);
q.push({pre[x],x,t.v + calc(pre[x],x,t.v),vis[x] = ++tot});
q.push({x,nxt[x],t.v + calc(x,nxt[x],t.v),vis[nxt[x]] = ++tot});
}
tot = 0;
while (!q.empty()) {
node2 t = q.top();
if (!(pre[t.x] < 0 || t.y != nxt[t.x] || vis[t.y] != t.w)) ans[++tot] = a[t.x].id;
q.pop();
}
sort(ans + 1,ans + tot + 1);
printf("%lld\n",tot);
for (int i = 1;i <= tot;i ++ ) printf("%lld ",ans[i]);
return 0;
}

浙公网安备 33010602011771号