LastWhisper最后的耳语

算法杂记 2023/02/26

WKL && YZY·2023-02-26 21:53·66 次阅读

算法杂记 2023/02/26

算法杂记 2023/02/26#

好久没有自己写出过一题了,尴尬。这题WA了足足 6 发才写出来。这种偏模拟一些的我还是太薄弱了。

Game on Axis 1900#

题面翻译#

题目描述#

n 个点,第 i 个点上有数字 ai,你在这些点上面玩一个游戏,初始在点 1。当你在点 i 时:

  • 1in,则你会跳到点 i+ai
  • 否则游戏结束。

在游戏开始前,你可以选择一对 xy1xn,nyn),并将 ax 赋值为 y。请求出这样的 (x,y) 的对数使得可以在有限步内结束游戏。注意即使 ax 已经等于 y 了,(x,y) 也可能是合法的。

输入格式#

输入第一行一个正整数 t1t104)表示数据组数。

每组测试数据第一行一个正整数 n1n2105)表示点的个数。

第二行 n 个正整数 a1,a2,,annain)表示序列 a

保证所有测试数据的 n 之和不超过 2105

样例解释#

第一组数据中,符合条件的 (x,y)(1,1)(1,1),分别对应路径 1012。注意 (1,2) 不合法因为 n=1y=2 不符合 nyn(1,0) 也不合法因为你将永远从 1 走到 1 而无法结束游戏。

第二组数据中,符合条件的 (x,y)(1,2),(1,1),(1,2),(2,2),(2,1),(2,0),(2,1),(2,2)

第三组数据中,符合条件的 (x,y)(1,2),(1,1),(1,1),(1,2),(2,2),(2,1),(2,2)

题目描述#

There are n points 1,2,,n , each point i has a number ai on it. You're playing a game on them. Initially, you are at point 1 . When you are at point i , take following steps:

  • If 1in , go to i+ai ,
  • Otherwise, the game ends.

Before the game begins, you can choose two integers x and y satisfying 1xn , nyn and replace ax with y (set ax:=y ). Find the number of distinct pairs (x,y) such that the game that you start after making the change ends in a finite number of steps.

Notice that you do not have to satisfy axy .

输入格式#

Each test contains multiple test cases. The first line contains an integer t ( 1t104) — the number of test cases.

The first line of each test case contains one integer n ( 1n2105 ) — the number of points.

The second line contains n integers a1,a2,,an ( nain ) — the numbers on the axis.

It's guaranteed that the sum of n does not exceed 2105 .

输出格式#

For each test case, print a line containing a single integer — the number of distinct pairs (x,y) with which the game ends.

样例 #1#

样例输入 #1#

Copy
9 1 0 2 -1 0 2 1 -1 2 1 1 3 -1 -2 -1 3 1 -2 -1 4 -1 4 -2 1 5 1 1 1 1 -4 5 1 1 1 1 1

样例输出 #1#

Copy
2 8 6 7 20 17 34 30 40

提示#

In the first test case, the pairs (x,y) with which the game ends are (1,1) and (1,1) , corresponding to the routes 10 and 12 . Note that (1,2) is invalid since when n=1 , y=2 violates nyn . (1,0) is also invalid since you will go from 1 to 1 forever.

In the second test case, the pairs are (1,2),(1,1),(1,2),(2,2),(2,1),(2,0),(2,1),(2,2) .

In the fourth test case, the pairs are (1,2),(1,1),(1,1),(1,2),(2,2),(2,1),(2,2) .

这题实际上是一个建图题。原始的图 G 存在一些边的集合:E={(xx+a[x])}。我们需要找到的是达到 0n 的位置。

定义 org 代表原始路径,org[i] 代表对应的节点。can 代表除了 org 外可以跳出去的节点的数量。

我们把他们分成两类节点,一类是 org 一类是除了 org 之外的节点。

我们通过 org_ok 来判断原始路径是否能直接跳出去。

如果 org_ok 的话,那么除了 org 外的所有节点都可以任意修改(每个节点都可以改 2×n+1)。否则,我们不能随意修改。

对于 org 上的节点,我们只能考虑:

  • 首先是,任何节点都有 n+1 种直接跳出去的方式。
  • 除此之外,考虑如果 org_ok 可以直接跳到后面
  • 最重要的是,我们得考虑 can 中是否包含一些在 org_ok 情况下,跳回 org 路径的情况,那么我们需要避免修改一些路径导致回路。比如 2 -> 4 -> 3 -> -5 并且存在 x -> 2 的情况,如果你修改 4->x 看起来好像你跳到了一个可以出去的路径,实际上跑的是一个环。所以我们需要预处理 in papreSum 用来剔除这些(其实实现略麻烦)

最后看代码吧,WA 了 N 发。

Copy
#include <bits/stdc++.h> using namespace std; #define DEBUG 0 // #define int long long #define pb push_back #define vt std::vector #define rep(i, l, r) for (ll i = (l); i < (r); ++ i) #define per(i, l, r) for (ll i = (l); i >= (r); -- i) using ll = long long; using pii = pair<int, int>; template<typename... Args> inline void _wpr_(Args... args) { std::cout << '\n'; } template<typename T, typename... Args> inline void _wpr_(T val, Args... args) { std::cout << " " << val; _wpr_(args...); } template<typename T, typename... Args> void wpr(T val, Args... args) { std::cout << val; _wpr_(args...); } const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; vt<int> global_ans; void solve(int th){ int n; std::cin >> n; vt<int> a(n + 1); rep (i, 1, n + 1) std::cin >> a[i]; vt<int> vis(n + 1, 0), org(n + 1, 0), f(n + 1, -1), pa(n + 1, 0), in(n + 1, 0); iota(all(pa), 0); // init 每个位置等于自己 int cnt = 0; function<int(int, int)> dfs = [&](int cur, int set_org){ ++ cnt; if (set_org) org[cur] = 1; int flag = 1, nxt = cur + a[cur]; if (nxt >= 1 && nxt <= n){ if (!vis[nxt]){ vis[nxt] = 1; flag = dfs(nxt, set_org); if (!set_org) pa[cur] = pa[nxt]; // 更新前缀 return f[cur] = flag; } else { if (f[nxt] == -1) return f[cur] = 0; // cycle // 一定不是第一次 set_org why? 因为第一次要么时 vis[nxt] = 0 或者 找到了 cycle flag = f[nxt]; // 之前已经计算过了 if (org[pa[nxt]]){ // 如果 nxt 是连接到原始路线上的,我们就需要计算这个前缀 pa[cur] = pa[nxt]; // 更新 in[pa[cur]] += cnt; // 把整个路径上的节点都加到原始节点上 } } } return f[cur] = flag; }; int org_ok = 0; // 是否有原始路线 int can = 0; // 除了原始路线之外,可以出去的节点数 ll ans = 0, base = 0; // base: 原始路线的长度 rep (i, 1, n + 1){ if (i == 1){ cnt = 0; vis[i] = 1; org_ok = dfs(i, 1); base = cnt; }else if (vis[i] == 0){ cnt = 0; vis[i] = 1; int flag = dfs(i, 0); if (flag == 1) can += cnt; } } if (org_ok) // 如果有原始路线,那么除了原始路线之外的节点都可以随意设置 ans += (n - base) * (2 * n + 1); ll preSum = 0; // 计算到当前原始路径点 cr 时,有多少 can 里面的节点是跳到 [1 -> cr] 的路径上,这些点是不能用的! for (int cr = 1, i = 0; i < base; ++ i){ ans += n + 1; // direct out (每个点都有 n + 1 种方法 直接出去) preSum += in[cr]; // 这里是计算,借助不在原始路线上的节点,进行跳出的方法数 if (org_ok) // 如果有原始路线,那么我们需要考虑 preSum 对 can 对影响 ans += max(0ll, can - preSum); else ans += can; // 否则,所有的 can 都可以出去 // 如果原始路线可以跳出,那么可以从 cr 节点直接往后面原始路径上的节点跳 if (org_ok){ ans += base - i - 1; // to nxt } cr = cr + a[cr]; // 更新 cr } wpr(ans); } signed main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int t = 1; global_ans.clear(); std::cin >> t; for (int th = 1; th <= t; ++ th) solve(th); return 0; }
posted @   Last_Whisper  阅读(66)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示
目录