专项测试 数学2


A. 猜拳游戏

可以分成两部分解决

一部分解决每一轮赢的概率,一部分解决最后获胜的概率

先看第一部分,发现每一轮平局不影响结果,于是忽略他,只考虑输赢的情况

\(p,r\) 分别为原来赢和输的概率,设 \(q=\frac{p}{p+r}\) 即现在每轮赢的概率

变化一下形式 \(\frac{p}{p+r}=1-\frac{r}{p+r}=1-\frac{1}{1+\frac{p}{r}}\)

于是最大化 \(\frac{p}{r}\) 即可,可以使用分数规划

二分一个 \(mid\) 和当前的 \(\frac{p}{r}\) 比较

\(\frac{p}{r}>mid \rightarrow p>mid\times r \rightarrow p-mid\times r>0\)

于是假设 \(A\) 赢一轮 \(+1\)\(B\) 赢一轮 \(-mid\)

从后往前 \(dp\) ,设 \(f_{i,j}\) 表示第 \(i\) 轮赢比输多 \(j\) 局的最大 \(p-mid\times r\)

然后一下看 \(f_{0,0}\) 是否大于 \(0\)

\(f_{n,x}\) 边界

  1. \(x>0\)\(1\)

  2. \(x<0\)\(-mid\)

  3. \(x=0\)\(0\)

再看第二部分,发现 \(m1+m2\) 的数量级很小于是直接高斯消元

\(a_i\) 表示多赢 \(i\) 轮的概率 \(a_i=a_{i-1}\times q+a_{i+1}\times (1-q)\)

注意一下边界

Code
#include
#define int long long
#define rint signed
#define eps 1e-8
#define O 1000
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m1,m2;
double a[1010][3],f[1010][2010],q;
inline bool check(double mid){
	double r1,r2,r3;
	memset(f,0,sizeof(f));
	for(int i=-n;i<=n;i++){if(i>0) f[n][i+O]=1;if(i==0) f[n][i+O]=0;if(i<0) f[n][i+O]=-mid;}
	for(int i=n;i;i--) for(int j=-i+1;j<=i-1;j++){
		r1=f[i][j+1+O]*a[i][1]+f[i][j+O]*a[i][0]+f[i][j-1+O]*a[i][2];//+a[i][1]-mid*a[i][2];
		r2=f[i][j+1+O]*a[i][2]+f[i][j+O]*a[i][1]+f[i][j-1+O]*a[i][0];//+a[i][2]-mid*a[i][0];
		r3=f[i][j+1+O]*a[i][0]+f[i][j+O]*a[i][2]+f[i][j-1+O]*a[i][1];//+a[i][0]-mid*a[i][1];
		f[i-1][j+O]=max({r1,r2,r3});
	}
	return f[0][0+O]>0;
}
namespace G{
	double mp[210][210];//+m2
	inline void gaussbuild(){
		memset(mp,0,sizeof(mp));
		for(int i=1;i<=m1+m2+1;i++) mp[i][i]=1;
		for(int i=1;imx) maxp=j,mx=fabs(mp[j][i]);
			if(i!=maxp) swap(mp[i],mp[maxp]);
			div=mp[i][i];
			for(int j=1;j<=n+1;j++) mp[i][j]/=div;
			for(int j=1;j<=n;j++){
				if(i==j) continue;
				if(mp[j][i]==0) continue;
				div=mp[j][i]/mp[i][i];
				for(int k=1;k<=n+1;k++) mp[j][k]=mp[j][k]-div*mp[i][k];
			}
		}
		return mp[n][n+1];
	}
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	while(1){
		n=read(),m1=read(),m2=read();if(!n) break;
		for(int i=1;i<=n;i++) a[i][0]=read()/100.0,a[i][2]=read()/100.0,a[i][1]=read()/100.0;
		double l=0,r=10000;
		while(r-l>eps){
			double mid=(l+r)/2;
			if(check(mid)) l=mid;
			else r=mid;
		}
		if(fabs(10000-l)

B. B君的回忆

\(mod\) 意义下一定存在循环节,于是直接打表找规律

得出如下规律

\(p\) 分解为 \(\prod\limits_{i=1}^{cnt}pri_i^{c_i}\) 的形式

于是他的循环节长度为 \(\prod\limits_{i=1}^{cnt}f(pri_i)\times pri_i^{c_i-1}\)

\(f(x)\) 表示 \(\mod x\) 意义下循环节的长度

对于每个 \(f(x)\) 可以直接用矩阵 \(bsgs\)

每个 \(g(n)\) 都可以变成 \(a^xc\) 的形式( \(a,c\) 均为矩阵 )

于是套 \(bsgs\) 的板子 \(a^x\equiv b(\mod p)\) 稍微变化一下就能求 \(a^xc\equiv b(\mod p)\)

最后把答案都乘起来,对于每一层都求一遍就行

Code
#include
#define int long long
#define rint signed
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int T,a,b,n,k,mod;
int tmod[110];
mapAAA;
struct mat{
	int a[4][4];
	inline mat operator*(mat const &b)const{
		mat c;memset(c.a,0,sizeof(c.a));
		for(int i=1;i<=2;i++) for(int k=1;k<=2;k++) for(int j=1;j<=2;j++) (c.a[i][j]+=a[i][k]*b.a[k][j])%=mod;
		return c;
	}
	inline bool operator<(const mat &b)const{for(int i=1;i<=2;i++) for(int j=1;j<=2;j++) if(a[i][j]!=b.a[i][j]) return a[i][j]>=1;}
	return res;
}
inline int g(int x){
	if(x==1) return b;if(x==0) return a;
	memset(B.a,0,sizeof(B.a));
	memset(A.a,0,sizeof(A.a));
	B.a[1][1]=b;B.a[2][1]=a;
	A.a[1][1]=3,A.a[1][2]=mod-1;
	A.a[2][1]=1,A.a[2][2]=0;
	A=qpow(A,x-1);B=A*B;
	return B.a[1][1];
}
namespace MODDDD{
	int prime[100],c[100],cnt,res;
	map mp;
	inline int bsgs(mat a,mat b,mat c,int p){
		if((qpow(a,p)*c)==b) return p;
		if((qpow(a,p-1)*c)==b) return p-1;
		if((qpow(a,p+1)*c)==b) return p+1;
		mp.clear();int t=sqrt(p)+1;mat w;memset(w.a,0,sizeof(w.a));for(int i=1;i<=2;i++) w.a[i][i]=1;
		for(int i=0;i<=t;i++,w=w*a) mp[w*b]=i;
		a=qpow(a,t);
		if(a.empty()) return (b.empty())?1:-1;
		memset(w.a,0,sizeof(w.a));for(int i=1;i<=2;i++) w.a[i][i]=1;
		for(int i=0,j;i<=t;i++,w=w*a){
			j=mp.find(w*c)==mp.end()?-1:mp[w*c];
			if(j>=0&&i*t-j>0) return i*t-j;
		}
		return -1;
	}
	inline int getP(int pri){
		mod=pri;
		memset(B.a,0,sizeof(B.a));
		memset(A.a,0,sizeof(A.a));
		B.a[1][1]=1;B.a[2][1]=0;
		A.a[1][1]=3,A.a[1][2]=mod-1;
		A.a[2][1]=1,A.a[2][2]=0;
		return bsgs(A,B,B,mod);
	}
	int gcd(int x,int y){return (!y)?x:gcd(y,x%y);}	
	inline int getmod(int p){
		if(AAA.find(p)!=AAA.end()) return AAA[p];
		int xxxx=p;
		cnt=0;res=1;
		for(int i=2;i*i<=p;i++) if(p%i==0){
			prime[++cnt]=i;c[cnt]=0;
			while(p%i==0) p/=i,c[cnt]++;
		}
		if(p>1) prime[++cnt]=p,c[cnt]=1;
		for(int i=1,gg,cc;i<=cnt;i++){
			cc=getP(prime[i]);
			cc=(cc==-1?prime[i]:cc);
			for(int j=1;j

C. 小 H 爱染色

要求 \(\sum\limits_{i=0}^{n-1}F(i)\times H(n-i)\)

球的编号从 \(0\)\(n-1\)

\(F(x)\) 为给定不超过 \(m\) 次的多项式

\(H(i)\) 为给 \(i\) 个球染色其中第一个球必须染

朴素的式子为 \(H(i)=2\binom{i}{m-1}\binom{i}{m}+\binom{i}{m-1}^2\) 没啥拓展性

再看上面的式子 \(\sum\limits_{i=0}^{n-1}F(i)\times H(n-i)\)

可以对于每个 \(j\) 求出答案于是变成,为了方便把 \(n-1\) 变成 \(n\)

\(\sum\limits_{j=0}^mf_j\sum\limits_{i=0}^nH(n-i)i^j\)

考虑 \(i^j\) 的组合意义

\(i\) 个球染色一次染一个一共染 \(j\)

于是可以把两个式子化在一起,直接枚举两部分各染了几个球分别记录为 \(t,k\) ,这样就不需要枚举 \(i\) 了因为第 \(i\) 个球是后 \(k\) 个里的第一个所以一定被染了

后面的方案数为 \(\binom{k}{m}\binom{m}{2m-k}\)

一共染 \(k\) 个,第一次先挑 \(m\) 个染,第二次先把没染的染了再从剩下的挑

前面的相当于 \(t\) 个球染 \(j\) 次,所有的都被染了,二项式反演一下就行

方案数为 \(\sum\limits_{i=0}^t\binom{t}{i}(-1)^{i}(t-i)^j\)

然后就变成了 \(\sum\limits_{j=0}^{m}f_j\sum\limits_{t=0}^m\sum\limits_{k=m}^{2m}\binom{n}{t+k}\binom{k}{m}\binom{m}{2m-k}\sum\limits_{i=0}^t\binom{t}{i}(-1)^{i}(t-i)^j\)

\(\binom{n}{t+k}\) 注意到 \(H(i)\) 为给 \(i\) 个球染色其中第一个球必须染的方案数,你不需要知道到底时哪 \(i\)

然后你总共会染 \(t+k\) 个,所以直接 \(\binom{n}{t+k}\)

发现 \(j\) 只在最后有用于是直接挪过去

\(\sum\limits_{t=0}^m\sum\limits_{k=m}^{2m}\binom{n}{t+k}\binom{k}{m}\binom{m}{2m-k}\sum\limits_{i=0}^t\binom{t}{i}(-1)^{i}\sum\limits_{j=0}^m(t-i)^j\)

\(\sum\limits_{j=0}^m(t-i)^j\) 这个东西就相当与给定的多项式第 \(t-i\)

然后剩下的就能卷积了,先卷 \(f(t)=\sum\limits_{i=0}^t\binom{t}{i}(-1)^{i}F(t-i)\) 再卷 \(\sum\limits_{t=0}^m\sum\limits_{k=m}^{2m}\binom{n}{t+k}\binom{k}{m}\binom{m}{2m-k}f(t)\)

Code
#include
#define int long long
#define rint signed
#define mod 998244353
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m,ans,m3,m2;
int f[4194310],g[4194310];
int fac[3000010],inv[3000010],C[3000010];
inline int Binom(int n,int m){return fac[n]*inv[m]%mod*inv[n-m]%mod;}
inline int qpow(int x,int k){
	int res=1,base=x;
	while(k){if(k&1) res=res*base%mod;base=base*base%mod;k>>=1;}
	return res;
}
inline void md(int &x){x=(x>=mod)?x-mod:x;}
inline void mmd(int &x){x=(x>=mod)?x%mod:x;}
namespace POLY{
	int len,L,INV;
	int r[4194310],G[4194310];
	inline void pre(int lim){
		for(len=1,L=0;len<=lim;len<<=1,L++);
		for(rint i=0;i>1]>>1)|((i&1)<<(L-1));
		G[0]=1,G[1]=qpow(3,(mod-1)/len);for(rint i=2;i<=len;++i) mmd(G[i]=G[i-1]*G[1]);
	}
	inline void rev(){G[0]=1,G[1]=qpow(G[1],mod-2);for(rint i=2;i<=len;++i) mmd(G[i]=G[i-1]*G[1]);}
	inline void ntt(int a[]){
		for(rint i=0;i>1;d>=1) for(rint i=0;i