CF2165B Marble Council

考虑最后生成的集合不同当且仅当存在某种数的出现次数不同。

假设数 \(i\) 出现 \(c_i\) 次,想在生成集合中出现 \(j\) 次:

  1. \(0<j\le c_i\) 必定可行,因为拿出 \(j\) 个数 \(i\) 分成新的集合,剩下的 \(i\) 任意放在这新的 \(j\) 个集合中没有影响。

  2. \(j=0\) 则需要将 \(c_i\)\(i\) 分到若干个集合中,但均需要保证出现次数 \(\le\) 该集合众数出现次数。

所以只有不出现在最终生成集合的元素有限制。而要满足这个限制对于 \(j>0\) 的数必定全部放入新的集合中更优(这样 \(c_i\) 全部计入集合众数出现次数之和)。

\(f_{i,j,k}\) 表示前 \(i\) 种数,所有集合众数出现次数之和为 \(j\),有 \(k\) 种元素限制 \(>j\)

转移即为:

· \(i\) 在生成序列中出现次数 >0。

\(f_{i,j,k}=\sum_{p\le \min(s,k)}f_{i-1,j-c_i,k-p}\cdot{s\choose p}\cdot c_i\)

其中 \(s\) 为前 \(i-1\) 种数中有多少 \(c\in(j-c_i,j]\),加入第 \(i\) 种数后这些数的限制可以被满足。\(p\) 即为枚举满足了多少限制,\({s\choose p}\) 为选出满足限制的数的种类,\(c_i\)\(i\) 在生成序列中可以出现 \([1,c_i]\) 次。

· \(i\) 在生成序列中出现次数 =0。

\(f_{i,j,k}=f_{i-1,j,k-[c_i>j]}\)

这个转移时简单的,表示第 \(i\) 种数会不会多一条限制。

code
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define fin(x) freopen(#x".in","r",stdin)
#define fout(x) freopen(#x".out","w",stdout)
#define fr(x) fin(x),fout(x);
#define Fr(x,y) fin(x),fout(y)
#define INPUT(_1,_2,FILE,...) FILE
#define IO(...) INPUT(__VA_ARGS__,Fr,fr)(__VA_ARGS__)
using namespace std;
using namespace __gnu_pbds;
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define cfast ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define ll long long
#define ull unsigned long long
#define intz(x,y) memset((x),(y),sizeof((x)))
char *p1,*p2,buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
#define tup(x) array<int,(x)>
inline ll read(){
    ll x=0,f=1;char ch=nc();
    while(ch<48||ch>57){if(ch=='-')f=-1;ch=nc();}
    while(ch>=48&&ch<=57)x=x*10+ch-48,ch=nc();
   	return x*f;
}
const int N=5005,mod=998244353;
int a[N],c[N],cnt[N][N];ll f[2][N][N],fac[N],ifac[N];
ll qp(ll x,int y){ll res=1;for(;y;y>>=1,x=x*x%mod)if(y&1)res=res*x%mod;return res;}
int C(int x,int y){return fac[x]*ifac[y]%mod*ifac[x-y]%mod;}
inline void UesugiErii(){
	int n;ll ans=0;cin>>n;
	for(int i=0;i<=n;i++){c[i]=0;
		for(int j=0;j<=n;j++)
			f[0][i][j]=f[1][i][j]=0,cnt[i][j]=0;
	}
	for(int i=1;i<=n;i++)
		cin>>a[i],++c[a[i]];
	f[0][0][0]=1;
	sort(c+1,c+1+n);
	for(int i=1;i<=n;cnt[i][c[i]]++,i++)
		for(int j=0;j<=n;j++)cnt[i][j]=cnt[i-1][j];
	for(int i=fac[0]=1;i<=n;i++)
		fac[i]=fac[i-1]*i%mod;
	ifac[n]=qp(fac[n],mod-2);
	for(int i=n;i;i--)
		ifac[i-1]=ifac[i]*i%mod;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cnt[i][j]+=cnt[i][j-1];
	int S=0;
	for(int i=1;i<=n;S+=c[i],i++){
		int t=i&1;
		for(int j=0;j<=S;j++)
			for(int k=0;k<=S/(j+1);k++)f[t][j][k]=0; 
		for(int j=0;j<=S;j++)
			for(int k=0;k<=S/(j+1);k++){
				int s=cnt[i-1][j+c[i]]-cnt[i-1][j];
				for(int p=0;p<=min(s,k);p++)
					(f[t][j+c[i]][k-p]+=C(s,p)*c[i]%mod*f[!t][j][k]%mod)%=mod;
				(f[t][j][k+(c[i]>j)]+=f[!t][j][k])%=mod;
			}
	}
	for(int i=1;i<=n;i++)
		(ans+=f[n&1][i][0])%=mod;
	cout<<ans<<'\n';
}
signed main(){
	//IO();
	cfast;
	int _=1;cin>>_;
	for(;_;_--)UesugiErii();
	return 0;
}

Time limit exceeded on test 7。

这份代码显然最劣情况是可以被卡到 \(O(n^3)\) 级别的。

\(k\) 的范围为 \([0,\frac{n}{j+1}]\)(因为能限制必定出现次数 >j),所以第二种转移是 \(O(n^2\ln n)\) 的。

瓶颈在第一种转移,考虑优化。但是第一种转移的式子优化非常困难。考虑去掉枚举 \(p\) 的过程。按 \(c\) 升序排序,那么如果 \(i\) 在生成序列出现次数 >0,那么以前的限制必定全部满足。所以直接从 \(f_{i-1,j,k}\) 转移到 \(f_{i,j+c_i,0}\) 即可。

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define fin(x) freopen(#x".in","r",stdin)
#define fout(x) freopen(#x".out","w",stdout)
#define fr(x) fin(x),fout(x);
#define Fr(x,y) fin(x),fout(y)
#define INPUT(_1,_2,FILE,...) FILE
#define IO(...) INPUT(__VA_ARGS__,Fr,fr)(__VA_ARGS__)
using namespace std;
using namespace __gnu_pbds;
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define cfast ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define ll long long
#define ull unsigned long long
#define intz(x,y) memset((x),(y),sizeof((x)))
char *p1,*p2,buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
#define tup(x) array<int,(x)>
inline ll read(){
    ll x=0,f=1;char ch=nc();
    while(ch<48||ch>57){if(ch=='-')f=-1;ch=nc();}
    while(ch>=48&&ch<=57)x=x*10+ch-48,ch=nc();
   	return x*f;
}
//void write(int x){cout<<x<<' ';}
//void write(pii x){cout<<"P("<<x.fi<<','<<x.se<<")\n";}
//void write(vector<auto>x){for(auto i:x)write(i);cout<<'\n';}
//void write(auto *a,int l,int r){for(int i=l;i<=r;i++)write(a[i]);cout<<'\n';}
inline ll lowbit(ll x){return x&-x;}
inline int pcount(ll x){
	for(int i=0,res=0;;res+=(x>>i)&1,i++)
		if(i>60)return res;
}
 
//struct mt{
//	ll v;
//	mt(){v=0;}
//	mt(int x){this->v=x;}
//	inline mt operator+(mt x){return {(v+x.v)%mod};}
//	inline mt operator-(mt x){return {(v+mod-x.v)%mod};}
//	inline mt operator*(mt x){return {1ll*v*x.v%mod};}
//};
//inline void add(mt &x,mt y){x=x+y;}
//mt qp(mt x,int y){mt res(1);for(;y;x=x*x,y>>=1)if(y&1)res=res*x;return res;}
const int N=5005,mod=998244353;
int a[N],c[N],cnt[N][N];ll f[2][N][N],fac[N],ifac[N];
ll qp(ll x,int y){ll res=1;for(;y;y>>=1,x=x*x%mod)if(y&1)res=res*x%mod;return res;}
int C(int x,int y){return fac[x]*ifac[y]%mod*ifac[x-y]%mod;}
inline void UesugiErii(){
	int n;ll ans=0;cin>>n;
	for(int i=0;i<=n;i++){c[i]=0;
		for(int j=0;j<=n;j++)
			f[0][i][j]=f[1][i][j]=0,cnt[i][j]=0;
	}
	for(int i=1;i<=n;i++)
		cin>>a[i],++c[a[i]];
	f[0][0][0]=1;
	sort(c+1,c+1+n);
	for(int i=1;i<=n;cnt[i][c[i]]++,i++)
		for(int j=0;j<=n;j++)cnt[i][j]=cnt[i-1][j];
	for(int i=fac[0]=1;i<=n;i++)
		fac[i]=fac[i-1]*i%mod;
	ifac[n]=qp(fac[n],mod-2);
	for(int i=n;i;i--)
		ifac[i-1]=ifac[i]*i%mod;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cnt[i][j]+=cnt[i][j-1];
	int S=0;
	for(int i=1;i<=n;S+=c[i],i++){
		int t=i&1;
		for(int j=0;j<=S;j++)
			for(int k=0;k<=S/(j+1);k++)f[t][j][k]=0; 
		for(int j=0;j<=S;j++){
			int s=cnt[i-1][j+c[i]]-cnt[i-1][j];
			for(int k=0;k<=S/(j+1);k++)
				(f[t][j+c[i]][0]+=C(s,k)*c[i]%mod*f[!t][j][k]%mod)%=mod, 
				(f[t][j][k+(c[i]>j)]+=f[!t][j][k])%=mod;
		}
	}
	for(int i=1;i<=n;i++)
		(ans+=f[n&1][i][0])%=mod;
	cout<<ans<<'\n';
}
signed main(){
	//IO();
	cfast;
	int _=1;cin>>_;
	for(;_;_--)UesugiErii();
	return 0;
}
posted @ 2025-11-28 09:45  Uesugi1  阅读(0)  评论(0)    收藏  举报