190811 CSP-S 2019 模拟

这篇博客主要解析了CSP-S 2019模拟考试中涉及的数学优化和组合计数问题。通过质因数分解进行容斥原理的应用,以及在邮票选择问题中使用动态规划(DP)来处理状态转移,同时讨论了在解决距离最优化问题时,如何利用最大权匹配策略,并指出在实际实现中需要采用BFS版本。
摘要由CSDN通过智能技术生成

diyiti

直接考虑把 n n n质因数分解,然后就是一个套路的容斥。
其实可以二进制压位枚举集合然后枚举其子集求容斥系数但能写dfs鬼才写那玩意儿
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int mod=998244353;
typedef long long ll;
typedef pair<int,int> pii;
inline int add(int a,int b){return (a+=b)<mod?a:a-mod;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline void Add(int&a,int b){(a+=b)<mod?a:a-=mod;}
inline void Dec(int&a,int b){(a-=b)<0?a+=mod:a;}
inline void Mul(int&a,int b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,Mul(a,a))if(p&1)Mul(ret,a);return ret;}
inline ll mul(ll a,ll b,ll md){return a*b-(ll)((long double)a*b/md)*md;}
inline ll ksm(ll a,ll p,ll md){ll ret=1;for(;p;p>>=1,a=mul(a,a,md))if(p&1)ret=mul(ret,a,md);return ret;}
int pri[14]={3,5,7,11,13,17,19,23,29,31,37,41,43,47};
inline bool test(int a,ll x,ll t,int s){
	if(ksm(a,x-1,x)!=1)return 0;
	ll f=ksm(a,t,x),pre=f;
	for(ri i=0;i<s;++i){
		f=mul(f,f,x);
		if(f==1&&pre!=1&&pre!=x-1)return 0;
		pre=f;
	}
	return 1;
}
inline bool miller_rabin(ll x){
	if(!(x&1))return x==2;
	ll t=x-1,s=0;
	while(!(t&1))t>>=1,++s;
	for(ri i=0;i<14;++i){
		if(x==pri[i])return 1;
		if(x==x/pri[i]*pri[i])return 0;
		if(!test(pri[i],x,t,s))return 0;
	}
	return 1;
}
const int N=35;
int a[N],tot=0,ans=0;
ll n;
inline void update(int c1,int c2,bool coe){int t=mul(c2,ksm(2,c1)-1);coe?Add(ans,t):Dec(ans,t);}
void dfs(int pos,int c1,int c2,bool coe){
	if(pos==tot+1)return update(c1,c2,coe);
	dfs(pos+1,mul(c1,a[pos]+1,mod-1),c2,coe);
	dfs(pos+1,mul(c1,a[pos],mod-1),mul(c2,2),coe^1);
	if(a[pos]^1)dfs(pos+1,mul(c1,a[pos]-1,mod-1),c2,coe);
}
inline void init(){
	for(ri i=2,up=min((ll)sqrt(n),1000000ll);i<=up;++i){
		if(n!=n/i*i)continue;
		++tot;
		while(n==n/i*i)++a[tot],n/=i;
	}
	if(n^1){
		if(miller_rabin(n))a[++tot]=1;
		else{
			ll t=sqrt(n);
			if(n==t*t)a[++tot]=2;
			else a[++tot]=1,a[++tot]=1;
		}
	}
}
int main(){
	#ifdef ldxcaicai
	freopen("lx.in","r",stdin);
	#endif
	cin>>n;
	init();
	dfs(1,1,1,1);
	cout<<ans;
	return 0;
}

dierti

f i , j , k f_{i,j,k} fi,j,k表示已经决定完前 i i i种的状态,前 i i i种总共选了 j j j张,且 max ⁡ { c o l l e c t i n g i − c i , 0 } − max ⁡ { c o l l e c t i n g i − c i , 0 } 4 = k \max\{collecting_i-c_i,0\}-\frac{\max\{collecting_i-c_i,0\}}4=k max{collectingici,0}4max{collectingici,0}=k的不考虑不同颜色间顺序时的期望。

然后可以枚举第 i i i张选几张,注意到 k ≤ 0 k\le0 k0时就不用转移了。

然后假设最后各邮票的选取个数为 a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1,a2,...,ak,那么考虑不同颜色间顺序的方案数为 ( a 1 + a 2 + ⋅ ⋅ ⋅ + a n ) ! a 1 ! a 2 ! a 3 ! . . . a n ! \frac{(a_1+a_2+\cdot\cdot\cdot+a_n)!}{a_1!a_2!a_3!...a_n!} a1!a2!a3!...an!(a1+a2++an)!
我们的 d p dp dp只能保留 a 1 + a 2 + ⋅ ⋅ ⋅ + a n a_1+a_2+\cdot\cdot\cdot+a_n a1+a2++an的值,因此 a i ! a_i! ai!这个系数要在 d p dp dp的时候加进去。

然后这题 d p dp dp时的边界条件极其神奇。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
    static char buf[rlen],*ib,*ob;
    (ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
    return ib==ob?-1:*ib++;
}
inline int read(){
   int ans=0;
    bool f=1;
    char ch=gc();
    while(!isdigit(ch))f^=ch=='-',ch=gc();
    while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
    return f?ans:-ans;
}
const int mod=1e9+7;
typedef long long ll;
inline int add(int a,int b){return (a+=b)<mod?a:a-mod;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline void Add(int&a,int b){(a+=b)<mod?a:a-=mod;}
inline void Dec(int&a,int b){(a-=b)<0?a+=mod:a;}
inline void Mul(int&a,int b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,Mul(a,a))if(p&1)Mul(ret,a);return ret;}
const int N=1605;
int n,q[105],cur=0,a[105],fac[N],ifac[N],f[11][1605][405],p[1605];
inline void init(int up=1600){
	fac[0]=fac[1]=ifac[0]=ifac[1]=1;
	for(ri i=2;i<=up;++i)fac[i]=mul(fac[i-1],i),ifac[i]=mul(ifac[mod-mod/i*i],mod-mod/i);
	for(ri i=2;i<=up;++i)Mul(ifac[i],ifac[i-1]);
}
int main(){
	#ifdef ldxcaicai
	freopen("lx.in","r",stdin);
	#endif
	init();
	n=read();
	int sum=0;
	for(ri i=1;i<=n;++i)sum+=(q[i]=read());
	sum=ksm(sum,mod-2);
	for(ri i=1;i<=n;++i)Mul(q[i],sum);
	sum=0;
	for(ri i=1;i<=n;++i)sum+=(a[i]=read());
	f[0][0][sum]=1;
	sum<<=2;
	for(ri t,i=1,pre=0;i<=n;++i,pre=sum){
		for(ri mt=1,j=0;j<=sum;++j,Mul(mt,q[i]))p[j]=mul(ifac[j],mt);
		for(ri trans,j=0;j<=sum;++j)for(ri k=1;k<=400;++k)if((trans=f[i-1][j][k]))
			for(ri tt,l=0,up=sum-j;l<=up;++l){
				tt=((l<a[i])?(l-a[i]):(l-a[i])/4)+a[i];
				if(k<=tt)break;
				(f[i][j+l][k-tt]+=(ll)p[l]*trans%mod)%=mod;
			}
	}
	int ans=1;
	for(ri t=0,i=1;i<=sum;++i,t=0){
		for(ri j=1;j<=400;++j)Add(t,f[n][i][j]);
		Add(ans,mul(fac[i],t));
	}
	cout<<ans;
	return 0;
}

disanti

考虑最后最优解一定满足前两个限制,只需最大化距离和即可。
由于 x , y x,y x,y坐标对称因此可以只考虑 x x x坐标的信息。
Δ = ∑ i , j ( x i + a i − x j − a j ) 2 − ( x i − x j ) 2 = ∑ i , j ( a i − a j ) 2 + 2 ( x i − x j ) ( a i − a j ) = C + 2 n ∑ i x i a i \begin{aligned}\Delta=&amp;\sum_{i,j}(x_i+a_i-x_j-a_j)^2-(x_i-x_j)^2\\=&amp;\sum_{i,j}(a_i-a_j)^2+2(x_i-x_j)(a_i-a_j)\\=&amp;C+2n\sum_{i}x_ia_i\end{aligned} Δ===i,j(xi+aixjaj)2(xixj)2i,j(aiaj)2+2(xixj)(aiaj)C+2nixiai
那么做一个最大权匹配即可
然而要写bfs版的才能过
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	bool f=1;
	char ch=gc();
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return f?ans:-ans;
}
typedef pair<int,int> pii;
const int N=505,inf=1e9;
int n,trans[N][N];
pii a[N],b[N];
namespace KM{
	int lx[N],ly[N],slk[N],mth[N],prt[N],pre[N];
	bool vs[N];
	inline int delta(int a,int b){return lx[a]+ly[b]-trans[a][b];}
	inline bool bfs(int p){
		mth[0]=p;
		for(ri i=1;i<=n;++i)vs[i]=0,pre[i]=0,slk[i]=0x3f3f3f3f;
		int x,y=0,dt,nxt;
		do{
			vs[y]=1,x=mth[y],dt=inf;
			for(ri t,i=1;i<=n;++i){
				if(!vs[i]){
					t=delta(x,i);
					if(t<slk[i])slk[i]=t,pre[i]=y;
					if(slk[i]<dt)dt=slk[i],nxt=i;
				}
			}
			for(ri i=0;i<=n;++i){
				if(vs[i])lx[mth[i]]-=dt,ly[i]+=dt;
				else slk[i]-=dt;
			}
			y=nxt;
		}while(mth[y]);
		while(y)mth[y]=mth[pre[y]],y=pre[y];
	}
	inline void solve(){
		for(ri i=1;i<=n;++i)bfs(i);
		for(ri i=1;i<=n;++i)prt[mth[i]]=i;
		for(ri i=1;i<=n;++i)cout<<prt[i]<<' ';
	}
}
int main(){
	#ifdef ldxcaicai
	freopen("lx.in","r",stdin);
	#endif
	n=read();
	for(ri i=1;i<=n;++i)a[i].fi=read(),a[i].se=read();
	for(ri i=1;i<=n;++i)b[i].fi=read(),b[i].se=read();
	for(ri i=1;i<=n;++i)for(ri j=1;j<=n;++j)trans[i][j]=a[i].fi*b[j].fi+a[i].se*b[j].se;
	puts("Yes");
	KM::solve();
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值