题意:n个数字,规定跳跃规则为 ,每次从1出发,可以执行一次操作(
)问可能的情况有几种;
分析:很好想到建图,把不在范围内的路径全连到 n+1 点上,然后分类讨论。
如果点 1 可以走到终点 n+1,那么 1)不在 1 路径上的点随便怎么样都行,2)其他的点不能走到他的后继(这里我们发现从 n+1 反向建边,x+1个点,x条边,必然是一个树,所以反向树形DP一下,减去子树即可)
如果 1 不能走到终点 n+1 ,分别思考 1 成链和成环 的情况,发现都可以总结为考虑 1 往前的路径的点,这些点1)要不超出本身,2)要不连到树上;
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 15;
const int mo = 998244353;
#define pb push_back
#define pii pair<int,int>
#define ft first
#define sd second
#define ffor(i,a,b,c) for(int i=(a);i<(b);i+=(c))
#define For(i,a,b,c) for(int i=(a);i<=(b);i+=(c))
#define rfor(i,a,b,c) for(int i=(a);i>(b);i-=(c))
#define Rfor(i,a,b,c) for(int i=(a);i>=(b);i-=(c))
#define all(x) (x).begin(), (x).end()
#define debug1(x) cerr<<"! "<<x<<endl;
#define debug2(x,y) cerr<<"# "<<x<<" "<<y<<endl;
void slv() {
int n; cin >> n;
vector<vector<int>>E(n + 2), R(n + 2);
vector<int>a(n + 1);
For(i, 1, n, 1) {
cin >> a[i];
int v = a[i] + i;
if (v < 1 || v > n) v = n + 1;
E[i].pb(v);
R[v].pb(i);
}
vector<int>vis(n + 2), sz(n + 2), rvis(n + 2);
function<void(int)>dfs = [&](int u) {
sz[u] = 1;
for (auto v : R[u]) {
vis[v] = 1;
dfs(v);
sz[u] += sz[v];
}
};
auto cal = [&](int k) {//超出范围的点,后来才发现是个定值
return n + 1;
};
dfs(n + 1);
function<int(int)>dig = [&](int u) {
int sm = 0; rvis[u] = 2;
for (auto v : E[u])if (!rvis[v]) {
sm++;
sm += dig(v);
}
return sm;
};
int sm = dig(1);
if (vis[1]) {
ll ans = 0;
For(i, 1, n, 1) {
if (rvis[i] == 2) {
ans += cal(i) + sz[n + 1] - sz[i] - 1;
} else {
ans += 2 * n + 1;
}
}
cout << ans << '\n';
} else {
vector<int>tmp;
For(i, 1, n, 1) if (rvis[i] == 2) {
tmp.pb(i);
}
ll ans = 0;
for (auto k : tmp) {
ans += cal(k);
ans += sz[n + 1] - 1;
}
cout << ans << '\n';
}
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
int t; cin >> t;
while (t--) {
slv();
}
}