题解:AT_agc079_a [AGC079A] Tree Contraction Sequence
SegmentTree_ · · 题解
设
设
:::info[证明]
用
在任意时刻,一个点的序列都是递增的,不然它接到
对点
操作
若
若
也就是说,
从左往右扫描
对区间
你发现这是一个树的拓扑序状物,根据经典结论,答案就是
场上比较唐,写了个树状数组查询后缀最小值,实际上用并查集就行了。
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+5;
#define ll long long
#define ull unsigned ll
#define lid (id << 1)
#define rid (id << 1 | 1)
typedef pair<int, int> pii;
mt19937_64 rnd(chrono::steady_clock::now().time_since_epoch().count());
namespace Pinball{
const int P = 998244353;
int n, fac[N], inv[N];
int qpow(int a, int b){
int r = 1;
while (b){
if (b & 1) r = (ll)r * a % P;
a = (ll)a * a % P;
b >>= 1;
}
return r;
}
int a[N], tr[N];
void add(int p, int k){
for (;p;p -= p & -p) tr[p] = min(tr[p], k);
}
int query(int p){
int r = 1e9;
for (;p <= n;p += p & -p) r = min(r, tr[p]);
return r;
}
int ans;
void Purgatory(){
cin >> n;
--n;
for (int i = 1;i <= n;i++) tr[i] = 1e9;
for (int i = 1;i <= n;i++){
cin >> a[i];
add(i, a[i]);
while (1){
int t = query(a[i]);
if (t == a[i]) break;
a[i] = t;
}
add(i, a[i]);
}
ans = 1;
for (int i = 1;i <= n;i++) ans = (ll)ans * i % P;
for (int i = 1;i <= n;i++) ans = (ll)ans * qpow(i - a[i] + 1, P - 2) % P;
cout << ans << '\n';
}
}
signed main(){
cin.tie(0)->sync_with_stdio(false);
int T = 1;
cin >> T;
while (T--) Pinball::Purgatory();
return 0;
}