CF1603E A Perfect Problem
题面传送门
感觉是个神仙题。有一堆性质。
首先发现可以排序。再算的时候用可重排列算算就好了。
然后发现排序之后那个子序列就是假的,直接算子串就好了。
然后发现子串也是假的,其实是个前缀。就可以写一个\(O(n^6)\)的大暴力dp
考虑对于一个\(a_i\),如果其小于\(i\),那么\(a_i\times\min<i\times \min\leq \sum\limits{a_i}\)
所以可以发现对于所有\(a_i\)要\(a_i\geq i\)
然后发现如果\(a_n=n\)的话那么底下无论取啥都不大行,除了全部是\(n\)。
所以只要考虑\(a_n=n+1\)的情况。
显然的只要全序列合法就好了,然后设\(dp_{i,j,k}\)为到了第\(i\)个,选了\(j\)个,和为\(k\)的方案数,这个dp就是\(O(n^4\ln n)\)的。
事实上最小的数很大,实测只有前20,所以复杂度变成了\(O(20^3\ln n)\)
code:
#include<bits/stdc++.h>
#include<cstdio>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define RI re int
#define ll long long
#define db double
#define lb long db
#define N (200+5)
#define M (N*50+5)
#define mod 998244353
#define Mod (mod-1)
#define eps (1e-9)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int n,m,k,p,A[N+5],Cnt,P,Ne,La,vis[22][N][N];ll dp[22][N][N],frc[N],Inv[N],Ans;
I ll mpow(ll x,int y=p-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%p),y>>=1,x=x*x%p;return Ans;}
I ll dfs(int x,int S,int w){
if(vis[x][S][w]) return dp[x][S][w];ll &Ns=dp[x][S][w];vis[x][S][w]=1;Ns=0;
if(x<m&&x>w)return 0;if(x==m){if(w^n) Ns=Inv[n-w];return Ns;}
for(RI i=0;i+w<=n&&S+i*(m-x)<=n-m+1;i++) Ns=(Ns+dfs(x+1,S+i*(m-x),w+i)*Inv[i])%p;return Ns;
}
int main(){
freopen("1.in","r",stdin);
RI i;scanf("%d%d",&n,&p);for(frc[0]=Inv[0]=i=1;i<=n;i++) frc[i]=frc[i-1]*i%p,Inv[i]=mpow(frc[i]);
for(i=max(n-20,1);i<=n+1;i++)Me(vis,0),m=n-i+1,Ans+=dfs(0,0,0);printf("%lld\n",Ans%p*frc[n]%p);
}