正题
题目链接:https://www.luogu.com.cn/problem/P7444
题目大意
一个长度为nnn的排列,已知每个cic_ici表示那个排列中mexmexmex为iii的区间个数。求满足条件的排列个数
1≤n≤5×105,ci≥0,∑i=1nci=n(n+1)2−11\leq n\leq 5\times 10^5,c_i\geq 0,\sum_{i=1}^nc_i=\frac{n(n+1)}{2}-11≤n≤5×105,ci≥0,∑i=1nci=2n(n+1)−1
解题思路
考虑一个朴素的dpdpdp,设fi,l,rf_{i,l,r}fi,l,r表示加入了1∼i1\sim i1∼i,然后最大区间是[l,r][l,r][l,r]时的方案。
那么每次插入一个数iii的时候如果ci=0c_i=0ci=0那么它一定在目前的最大区间里,否则需要扩展到区间外,每次有往左或者往右扩展。
不难发现对于1∼i1\sim i1∼i扩展到lll,那么rrr是固定的,所以我们可以直接用fi,lf_{i,l}fi,l来表示状态,但是这样还是O(n2)O(n^2)O(n2)的,其实状态数比较少的,因为对于一个iii来说需要扩展的aia_iai都是一些倍数形的。
其实总状态数不会超过∑i=1nai\sum_{i=1}^n\sqrt{a_i}∑i=1nai,因为上限很难到,所以用个vectorvectorvector记录一下状态就能够过了
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const ll N=5e5+10,P=998244353;
ll n,a[N],f[2][N],ed[2][N],vis[N];
vector<int> v[2];
signed main()
{scanf("%lld",&n);for(ll i=0;i<n;i++)scanf("%lld",&a[i]);ll Ans=0,s=0;for(ll i=1;i<=n;i++){ll l=i-1,r=n-i;if(l*(l+1)+r*(r+1)==a[0]*2)s=i,Ans++;}if(!Ans)return puts("0")&0;f[0][s]=1;ed[0][s]=s;v[0].push_back(s);for(ll i=1;i<n-1;i++){v[i&1].clear();for(ll p=0;p<v[~i&1].size();p++){ll l=v[~i&1][p],r=ed[~i&1][l];if(vis[l]==i)continue;vis[l]=i;if(!a[i]){if(r-l-i<0)continue;(f[i&1][l]+=f[~i&1][l]*(r-l-i+1)%P)%=P;ed[i&1][l]=r;v[i&1].push_back(l);}else{ll lk=l,rk=n-r+1;if(a[i]%lk==0){ll nr=r+a[i]/lk;(f[i&1][l]+=f[~i&1][l])%=P;ed[i&1][l]=nr;v[i&1].push_back(l);} if(a[i]%rk==0){ll nl=l-a[i]/rk;(f[i&1][nl]+=f[~i&1][l])%=P;ed[i&1][nl]=r;v[i&1].push_back(nl);}}}for(ll p=0;p<v[~i&1].size();p++)f[~i&1][v[~i&1][p]]=0;}ll ans=0;for(ll i=1;i<=n;i++)(ans+=f[(n-2)&1][i])%=P;printf("%lld\n",ans*Ans%P);return 0;
}