QOJ 8351 Ruin the legend
https://qoj.ac/contest/1644/problem/8351
思路
首先我们可以容易发现,对于题目中这样的条件
注意到由于序列
直接统计答案十分困难,我们考虑容斥定理。
我们设
则有
- 对于一条长度为
i 的链,我们要在其中选择j 条边,有多少种构成连通块的方案?
这个是可以预处理的,我们令
- 不连接
i 和i+1 。因为不连接
i+1 ,所以这就是一个新的块,那么就有h_{i+1,j,0}=h_{i,j,0}+h_{i,j,1}
- 连接
i 和i+1 。我们加了一条新的
i+1 这个新边,那么i+1 和i 的贡献其实是并到了一起,但是注意到我们可以把i 所在的块“翻转”或“不翻转”,所以我们h_{i,j,0} 要统计两次。所以更新就有h_{i+1,j+1,1}=h_{i,j,1}+2h_{i,j,0} 。
对于第
#include<bits/stdc++.h>
#define int long long
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define per(i,r,l) for(int i=r;i>=l;--i)
using namespace std;
const int N=5010,p=998244353;
int n,k;
vector<int>a,fac,v;
int h[N][N][2];
int f[N][N];
bitset<N>vis;
map<int,int> mp;
void init(){
a.resize(n+2);
fac.resize(n+2);
fac[0]=1;
rep(i,1,n) fac[i]=fac[i-1]*i%p;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
cin>>n>>k; init();
rep(i,1,n){
cin>>a[i];
mp[a[i]]=i;
}
h[1][0][0]=1;
rep(i,1,n){
rep(j,0,i-1){
int s0=h[i][j][0];
int s1=h[i][j][1];
h[i+1][j][0]=(s0+s1)%p;
h[i+1][j+1][1]=(s0*2+s1)%p;
}
}
rep(i,1,n){
if(vis[i])continue;
int x=a[i],len=0;
for(;mp.count(x);x+=k){
vis[mp[x]]=1;
len++;
}
v.push_back(len);
}
int i=0;
f[0][0]=1;
int m=0;
for(auto len:v){
++i;
rep(j,0,m){
rep(d,0,len-1){
f[i][j+d]=(f[i][j+d]+f[i-1][j]*(h[len][d][0]+h[len][d][1]))%p;
}
}
m+=len-1;
}
int ans=0;
rep(j,0,m){
int v=(f[i][j]*fac[n-j])%p;
if(j%2==1){
ans=(ans-v+p)%p;
}else{
ans=(ans+v)%p;
}
}
cout<<ans<<'\n';
return 0;
}