CF1603E A Perfect Problem gxy001

注:完美为 perfect,好的为 good。好的序列为 $\min\times\max\ge\sum$ 的序列,完美的序列为每个子序列都好的序列。

首先,我们考虑排好序的完美序列,任意完美的序列的任意排列都是完美的。

然后我们发现:

定理 1 一个排好序的序列是完美的当且仅当其每个子区间都是好的。

证明:枚举每个 $i$ 和 $j$,我们发现 $\min\times\max=a_i\times a_j$ 固定 $\sum$ 最大时即为选择整个子区间 $[i,j]$ 时。

定理 2 一个排好序的序列是完美的当且仅当其每个前缀都是好的。

证明:此时,$a_ia_j\ge a_1a_j\ge \sum\limits_{k=1}^j a_j\ge \sum\limits_{k=i}^j a_j$,则有每个前缀是好的可以推出每个子区间是好的。

定理 3 $\forall k,a_k\ge k$。

证明:若 $a_k<k$,$\sum\limits_{i=1}^ka_i\ge ka_1>a_1a_k$,违反定理 2。

定理 4 若 $a_k=k$,则 $\forall i\le k,a_i=k$。

证明:由定理 2 $a_1a_k=ka_1\ge \sum\limits_{i=1}^ka_i$,又因为 $\min\limits_{i=1}^ka_i=a_1$,所以 $ka_1\le \sum\limits_{i=1}^ka_i$,所以 $ka_1=\sum\limits_{i=1}^ka_i$ ,所以 $\forall i\le k,a_i=k$。

定理 5 若 $a_n=n+1,\exist i<n,a_i\ge i+1$,且 $a_{1\sim n}$ 为好序列,则前缀 $a_{1\sim i}$ 为好序列。

证明:$(n+1)a_1\ge \sum\limits_{k=1}^n a_k$,则 $a_1\ge \sum\limits_{k=1}^n (a_k-a_1)\ge\sum\limits_{k=1}^i (a_k-a_1)$,则 $a_ia_1\ge (i+1)a_1\ge \sum\limits_{k=1}^i a_k$。

分两个情况讨论 $a_n=n$ 和 $a_n=n+1$,前一种情况是平凡的(因为定理 4),我们只考虑 $a_n=n+1$,则此时 $a$ 为完美的充要条件为:

  • $\forall i\le a_1,a_1\le a_i\le n+1$;
  • $\forall i>a_1,i+1\le a_i\le n+1$;
  • $\sum\limits_{i=1}^n(a_i-a_1)\le a_1$。

但是,我们要求的序列是没有排序的,让我们把每个数都减去 $a_1$,然后称之为 $b$。

枚举 $a_1$,则我们要计数这样的数列 $b$。

  • $0\le b_i\le n+1-a_1$;
  • $\sum b\le a_1$;
  • 至少存在 $i(1\le i\le n-a_1)$ 个数 $\ge n+2-i-a_1$。

我们设 $f_{i,s,k}$ 为已经确定 $i$ 个元素,当前和为 $s$,要放置值等于 $k$ 的数的方案数,枚举放了几个 $k$,$f_{i,s,k}=\sum\limits_{j}\frac{f_{i+j,s+jk,k-1}}{j!}$,边界为 $i=n$ 时,值为 $n!$ 。

DP 应该是 $O(n^3\log n)$($\log$ 是枚举 $j$ 的调和级数),加上枚举 $a_i$ 的复杂度,复杂度为 $O(n^4\log n)$。

定理 6 $a_1<n-2\sqrt n$ 时,答案为 $0$。

证明:因为条件至少存在 $i(1\le i\le n-a_1)$ 个数 $\ge n+2-i-a_1$,若 $a_1<n-2\sqrt n$,$\sum b_i>n+1>a_1$,所以无解。

复杂度 $O(n^3\sqrt n\log n)$,可以通过本题。

#include<cstdio>
#include<cmath>
#include<algorithm>
typedef unsigned long long ull;
typedef __uint128_t L;
struct FastMod {
    ull b, m;
    FastMod(ull b) : b(b), m(ull((L(1) << 64) / b)) {}
    ull operator ()(ull a) {
        ull q = (ull)((L(m) * a) >> 64);
        ull r = a - q * b; // can be proven that 0 <= r < 2*b
        return r >= b ? r - b : r;
    }
}mod(2);
int p,n,fac[210],ifac[210];
int pow(int x,int y){
	int res=1;
	while(y){
		if(y&1) res=mod(1ll*res*x);
		x=mod(1ll*x*x),y>>=1; 
	}
	return res;
}
int main(){
	scanf("%d%d",&n,&p);
	mod=FastMod(p);
	fac[0]=1;
	for(int i=1;i<=n;i++) fac[i]=mod(1ll*fac[i-1]*i);
	ifac[n]=pow(fac[n],p-2);
	for(int i=n;i;i--) ifac[i-1]=mod(1ll*ifac[i]*i);
	int ans=0;
	int lim=2*sqrt(n)+1;
	for(int a1=std::max(1,n-lim);a1<=n;a1++){
		static int f[210][210][210];
		for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) for(int k=0;k<=n;k++) f[i][j][k]=0;
		for(int i=0;i<=n;i++)
			for(int sum=0;sum<=a1;sum++)
				f[i][sum][0]=mod(1ll*fac[n]*ifac[n-i]);
		for(int k=1;k<=n+1-a1;k++){
			for(int i=0;i<n;i++)
				for(int sum=0;sum<=a1;sum++){
					int &ans=f[i][sum][k];
					int r=(a1-sum)/k;
					for(int cnt=std::min(r,n-i);~cnt;--cnt)if(k<=1||i+cnt>=n-a1+2-k){
						ans=mod(ans+1ll*f[i+cnt][sum+cnt*k][k-1]*ifac[cnt]);
					}
				}
			for(int sum=0;sum<=a1;sum++) f[n][sum][k]=fac[n];
		}
		ans=mod(ans+f[0][0][n+1-a1]);
	}
	printf("%d\n",ans);
	return 0;
}