一个重要的思想是:如果我进行了一次 " 值为 a 的单点加 ",然后我进行了一次 " 值为 b 的全局乘 ",那么我可以看作是进行了 b 次 " 值为 a 的单点加 "。
记 " 函数 i 进行了多少次 " 为 fi,对于每次调用,先在节点上打上标记,最后再用拓扑排序向下传递贡献。
具体地,倒序处理每一个调用(因为只有时间更靠后的 " 全局乘 " 才能影响到 " 单点加 "),假设说我这次要调用第 i 个函数,那么:
令 fi←fi+b。
令 b←b×muli。
然后考虑拓扑排序,假设说我这次要处理第 i 个函数,那么:
若 Ti=1,则令 kPi←kPi+Vi×fi。
若 Ti=2,则无视该操作。
若 Ti=3,则倒序处理每一个调用,假设说我现在要将贡献传递给函数 j,那么:
令 fj←fj+fi。
令 fi←fi×mulj。
拓扑排序完直接输出 ai×b+ki 即可。
时间复杂度 O(n+m+Q)。
#include<cstdio>#include<cstring>#include<algorithm>#include<queue>#include<vector>
using namespace std;
inlineintread() {
int x = 0, f = 1; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -f; s = getchar(); }
while (s >= '0' && s <= '9') { x = x * 10 + s - '0'; s = getchar(); }
return x * f;
}
constint N = 100100;
constint mod = 998244353;
int n;
int a[N];
int m;
structoperation {int opt;
int pos, val;
vector<int> g;
} T[N];
int mul[N];
voidcalc(int u) {
if (mul[u] != -1) return;
switch (T[u].opt) {
case1: {
mul[u] = 1;
break;
}
case2: {
mul[u] = T[u].val;
break;
}
case3: {
mul[u] = 1;
for (int i = 0; i < (int)T[u].g.size(); i ++) {
int v = T[u].g[i];
calc(v);
mul[u] = 1ll * mul[u] * mul[v] % mod;
}
break;
}
}
}
int Q;
int idx[N];
int b;
int f[N];
int deg[N];
int k[N];
voidtopsort() {
queue<int> q;
for (int i = 1; i <= m; i ++)
if (deg[i] == 0) q.push(i);
while (q.size()) {
int u = q.front(); q.pop();
switch (T[u].opt) {
case1: {
k[T[u].pos] = (k[T[u].pos] + 1ll * f[u] * T[u].val) % mod;
break;
}
case2: {
break;
}
case3: {
for (int j = T[u].g.size() - 1; j >= 0; j --) {
int v = T[u].g[j];
f[v] = (f[v] + f[u]) % mod;
f[u] = 1ll * f[u] * mul[v] % mod;
if (-- deg[v] == 0) q.push(v);
}
break;
}
}
}
}
intmain() {
n = read();
for (int i = 1; i <= n; i ++)
a[i] = read();
m = read();
for (int i = 1; i <= m; i ++) {
T[i].opt = read();
switch (T[i].opt) {
case1: {
T[i].pos = read(), T[i].val = read();
break;
}
case2: {
T[i].val = read();
break;
}
case3: {
int C = read();
for (int j = 1; j <= C; j ++) {
int x = read();
T[i].g.push_back(x);
}
break;
}
}
}
memset(mul, -1, sizeof(mul));
for (int i = 1; i <= m; i ++)
if (mul[i] == -1) calc(i);
Q = read();
for (int i = 1; i <= Q; i ++)
idx[i] = read();
b = 1;
for (int i = Q; i >= 1; i --) {
switch (T[idx[i]].opt) {
case1: {
f[idx[i]] = (f[idx[i]] + b) % mod;
break;
}
case2: {
b = 1ll * b * T[idx[i]].val % mod;
break;
}
case3: {
f[idx[i]] = (f[idx[i]] + b) % mod;
b = 1ll * b * mul[idx[i]] % mod;
break;
}
}
}
for (int u = 1; u <= m; u ++) {
if (T[u].opt != 3) continue;
for (int j = 0; j < (int)T[u].g.size(); j ++) {
int v = T[u].g[j];
deg[v] ++;
}
}
topsort();
for (int i = 1; i <= n; i ++)
printf("%d ", (1ll * a[i] * b + k[i]) % mod);
puts("");
return0;
}
(2):如果这个最强蛇 A 操作后会有另一条蛇 B 取代了它最强的位置。由于我们假设的大前提,蛇 A 不会成为这次蛇 B 操作的目标。根据引理 1,蛇 B 进行该次操作后,强度一定不如操作过后的蛇 A。故蛇 B 成为狩猎对象的优先级一定高于蛇 A。在题目给定的 " 所有蛇均采取最优策略 " 的博弈背景下,蛇 B 会在不让自己死的前提下采取最优策略,那么蛇 A 肯定也不会死。
在 Part 1 中:我们每次取出最强蛇与最弱蛇,然后进行该次操作,新产生的蛇直接放到 Q2 的队尾即可,根据引理 1,不失单调性。
在 Part 2 中:我们每次取出最强蛇与最弱蛇,先是判断该次操作后最强蛇是否会成为最弱蛇,若最强蛇会成为最弱蛇,那么将新产生的蛇直接放到 Q2 的队尾即可,根据最弱蛇的定义,不失单调性。
这样就可以使用队列来维护蛇了,回答单次询问的时间复杂度是 O(n) 的,十分优秀。
#include<cstdio>#include<cstring>#include<algorithm>#include<deque>
using namespace std;
inlineintread() {
int x = 0, f = 1; char s = getchar();
while (s < '0' || s > '9') { s = getchar(); }
while (s >= '0' && s <= '9') { x = x * 10 + s - '0'; s = getchar(); }
return x * f;
}
constint N = 1000100;
int T;
int n, m;
structNode {int val;
int id;
Node() { val = id = 0; }
Node(int X, int Y) : val(X), id(Y) {}
} a[N];
bool operator < (Node a, Node b) {
if (a.val != b.val) return a.val < b.val;
return a.id < b.id;
}
bool operator == (Node a, Node b) {
return a.id == b.id;
}
bool operator > (Node a, Node b) {
if (a.val != b.val) return a.val > b.val;
return a.id > b.id;
}
voidmakedata(int id) {
if (id == 1) {
n = read();
for (int i = 1; i <= n; i ++) {
a[i].val = read();
a[i].id = i;
}
} else {
m = read();
for (int i = 1; i <= m; i ++) {
int x = read(), y = read();
a[x].val = y;
}
}
}
deque<Node> p, q;
intGetMax() {
int id = 0; Node val = (Node) { -0x3f3f3f3f, 0 };
if (p.size() && p.front() > val) id = 1, val = p.front();
if (q.size() && q.front() > val) id = 2, val = q.front();
return id;
}
intGetMin() {
int id = 0; Node val = (Node) { 0x3f3f3f3f, n + 1 };
if (p.size() && p.back() < val) id = 1, val = p.back();
if (q.size() && q.back() < val) id = 2, val = q.back();
return id;
}
voidwork() {
while (p.size()) p.pop_back();
while (q.size()) q.pop_back();
for (int i = n; i >= 1; i --)
p.push_back(a[i]);
int now = 1;
for (; now < n; now ++) {
if (now == n - 1) break;
int Max = GetMax(), Min = GetMin();
Node X, Y;
if (Max == 1) X = p.front();
if (Max == 2) X = q.front();
if (Min == 1) Y = p.back();
if (Min == 2) Y = q.back();
Node G;
G.val = X.val - Y.val;
G.id = X.id;
if (X == Y) {
if (Max == 1) p.pop_front();
if (Max == 2) q.pop_front();
} else {
if (Max == 1) p.pop_front();
if (Max == 2) q.pop_front();
if (Min == 1) p.pop_back();
if (Min == 2) q.pop_back();
}
int id = GetMin();
Node cur;
if (id == 1) cur = p.back();
if (id == 2) cur = q.back();
if (G < cur) {
if (X == Y) {
if (Max == 1) p.push_front(X);
if (Max == 2) q.push_front(X);
} else {
if (Max == 1) p.push_front(X);
if (Max == 2) q.push_front(X);
if (Min == 1) p.push_back(Y);
if (Min == 2) q.push_back(Y);
}
break;
} else {
q.push_back(G);
}
}
if (now == n - 1) {
puts("1");
return;
}
int ans = n - now + 1;
int round = 0;
for (; now < n; now ++) {
round ++;
if (now == n - 1) {
round ++;
break;
}
int Max = GetMax(), Min = GetMin();
Node X, Y;
if (Max == 1) X = p.front();
if (Max == 2) X = q.front();
if (Min == 1) Y = p.back();
if (Min == 2) Y = q.back();
Node G;
G.val = X.val - Y.val;
G.id = X.id;
if (X == Y) {
if (Max == 1) p.pop_front();
if (Max == 2) q.pop_front();
} else {
if (Max == 1) p.pop_front();
if (Max == 2) q.pop_front();
if (Min == 1) p.pop_back();
if (Min == 2) q.pop_back();
}
int id = GetMin();
Node cur;
if (id == 1) cur = p.back();
if (id == 2) cur = q.back();
if (G < cur) {
q.push_back(G);
} else {
round --;
break;
}
}
if (round % 2 == 0) ans --;
printf("%d\n", ans);
}
intmain() {
T = read();
for (int i = 1; i <= T; i ++) {
makedata(i);
work();
}
return0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探