题解:P16923 [JLCPC 2026] 水晶城堡
题解区做法有点单一啊,来点做法整合。
统一步骤:先考虑一个长度为
方法 1:
不妨将
注意到:记
如果你注意到了上面的东西,那么这道题在十分钟内即可通过,否则,你会像我一样死磕一整场 /ll。
接着上面的步骤,记
解释一下最后一步:从颜色为
之后用莫队维护即可。
方法 2:
来自 JiaoYou。
一个长度为
期望即为:
最后一步是因为每个位置的概率实际上是相同的。
考虑计算
一种化简方式是:
另一种化简方式是:
之后用莫队维护即可。
方法 3:
来自 AuCodingFrogHoward。
一个不需要用连续段转换技巧的方法。
我们将颜色段数量按照颜色拆开,对于每个颜色,枚举它能构成的颜色段个数,并统计满足条件的排列数,相乘后相加即可。
记
那么,我们有:
解释一下,先把
这里要莫队维护还需要多拆一下式子。
到这里就可以用莫队维护了。
当然,你也可以进一步的拆成:
其实这个和上述方法等价。
方法 4:
和上面的方法 3 大致相同,既然我们划分颜色段的时候都没有考虑顺序,那就可以直接按照相同元素之间没有顺序区分来考虑。
那么,我们有:
需要注意的是,不同元素之间仍有区别,于是需要乘上
细心的你容易发现,这个式子化简一下就和上面的方法 3 没有区别了。
只写了方法 1,不过其他的也差不多。
莫队复杂度为
:::success[代码(方法 1)]
/*
Start Code At 2026/8/9 8:25:08 .
Code by Bestart (luogu uid : 1062290) .
*/
#include <bits/stdc++.h>
#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , fread_cnt , stdin) , p1 == p2) ? EOF : *p1 ++)
using namespace std ;
using db = double ;
using i128 = __int128 ;
using ll = long long ;
using ldb = long double ;
using uint = unsigned int ;
using ui128 = unsigned __int128 ;
using ull = unsigned long long ;
#define inline
//#define int long long
mt19937 rnd( chrono :: system_clock :: now() .time_since_epoch() .count() ) ;
const uint fread_cnt = 1 << 20 | 10 ;
const uint mod = 998244353 ;
const uint MN = 1e5 + 10 ;
const uint MQ = 1e5 + 10 ;
const int inf = 1e9 + 7 ;
//const ll inf = 1e18 ;
char buf[fread_cnt] , *p1 = buf , *p2 = buf ;
template <typename T = int>
inline T read()
{
bool f = 0 ; T x = 0 ; char ch = gc() ;
while(ch < '0' || ch > '9') f ^= ch == '-' , ch = gc() ;
while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch ^ 48) , ch = gc() ;
return f ? -x : x ;
}
inline uint qpow (ull a , ull b = mod - 2 , uint p = mod)
{
uint res = 1 ;
while(b)
{
if(b & 1) res = res * a % p ;
a = a * a % p , b >>= 1 ;
}
return res ;
}
uint ji[MN] , nji[MN] ;
inline void init (uint n = MN-1)
{
ji[0] = nji[0] = 1 ;
for(uint i = 1 ; i <= n ; ++ i) ji[i] = 1ull * ji[i-1] * i % mod ;
for(uint i = 1 ; i <= n ; ++ i) nji[i] = qpow(ji[i]) ;
}
uint n , m , bize , res , a[MN] , t[MN] , bl[MN] , ans[MQ] ;
struct ques {
uint l , r , ide ;
} q[MQ] ;
inline bool cmp (ques q1 , ques q2)
{
return bl[q1.l] == bl[q2.l] ? bl[q1.l] & 1 ? q1.r < q2.r : q1.r > q2.r : bl[q1.l] < bl[q2.l] ;
}
inline void add (uint x)
{
res = (res + t[a[x]]) % mod , ++ t[a[x]] ;
}
inline void del (uint x)
{
-- t[a[x]] , res = (res + mod - t[a[x]]) % mod ;
}
inline bool solve()
{
for(uint i = 1 ; i <= n ; ++ i) a[i] = bl[i] = t[i] = 0 ;
for(uint i = 1 ; i <= m ; ++ i) ans[i] = q[i] .l = q[i] .r = q[i] .ide = 0 ;
n = m = bize = res = 0 ;
cin >> n >> m , bize = n / sqrt(m) ;
for(uint i = 1 ; i <= n ; ++ i) cin >> a[i] , bl[i] = i < bize ? 1 : bl[i-bize] + 1 ;
for(uint i = 1 ; i <= m ; ++ i) cin >> q[i] .l >> q[i] .r , q[i] .ide = i ;
sort(q+1 , q+m+1 , cmp) ;
res = 0 ;
for(uint i = 1 , l = 1 , r = 0 ; i <= m ; ++ i)
{
while(r < q[i] .r) add(++r) ;
while(l > q[i] .l) add(--l) ;
while(r > q[i] .r) del(r--) ;
while(l < q[i] .l) del(l++) ;
ans[q[i] .ide] = (r-l+1 + mod - res * 2ull * ji[r-l+1-1] % mod * nji[r-l+1] % mod) % mod ;
}
for(uint i = 1 ; i <= m ; ++ i) cout << ans[i] << '\n' ;
return 0 ;
}
signed main()
{
// freopen("rune.in" , "r" , stdin) ;
// freopen("rune.out" , "w" , stdout) ;
cin.tie(0) -> sync_with_stdio(0) ;
// system("fc .out .ans") ;
int t = 1 ; init(1e5) , cin >> t ; while(t --) solve() ;
// while(!solve()) ;
// solve() ;
return 0 ;
}
:::