Achen模拟赛

良心的noip模拟赛


首先感谢队爷Achen学长为我们出的一套"良心的noip模拟题"(原话如此)。
虽说他总是自称蒟(A m o r p h o p h a l m s \tiny morphophalms morphophalms)蒻(k o n j a c \tiny onjac onjac),但是这改变不了他总是AK的事实。
确实是一套良(du)心(liu)的题目。


T 1 T1 T1

在这里插入图片描述
在这里插入图片描述
题面如此。
数位Dp可能是第一想法,但是位数高达 1 0 5 10^5 105级别的数位Dp确实不太科学。
针对题目的要求,发现可以使用并查集来维护不同位之间的关系。
考虑直接枚举原数的每一个前缀并且接下来一个数比原来小,这种前提下的方案数就是剩下位置的任意选择。
显然,选择方案就是 1 0 之 前 还 没 有 被 限 制 块 的 个 数 10^{\tiny 之前还没有被限制块的个数} 10
如果发现在满足当前前缀的前提下不能满足之后位数的限制,就结束循环。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int Maxn=100005;
const int mod=19260817;
inline int read() {
	char c; int rec=0;
	while((c=getchar())<'0'||c>'9');
	while(c>='0'&&c<='9') rec=rec*10+c-'0',c=getchar();
	return rec;
}
int n,m,a[Maxn],b[Maxn],fa[Maxn];
inline int getfa(int x) {return x==fa[x]?x:fa[x]=getfa(fa[x]);}
inline int Ksm(int a,int x) {
	int rec=1; while(x) { if(x&1) rec=1ll*rec*a%mod; a=1ll*a*a%mod; x>>=1;} return rec;
}
inline int Sov(int *x) {
	int rec=0,cnt=0;
	for(int i=1;i<=n;++i) if(fa[i]==i) ++cnt;
	for(int i=n;i;--i) {
		if(fa[i]==i) rec=(rec+x[i]*Ksm(10,--cnt))%mod;
		if(x[getfa(i)]<x[i]) rec=(rec+Ksm(10,cnt))%mod;
		if(x[getfa(i)]!=x[i]) break;
	} return rec;
}
int main() {
	n=read();
	for(int i=1;i<=n;++i) a[i]=read();
	for(int i=1;i<=n;++i) b[i]=read();
	for(int i=1;i<=n;++i) fa[i]=i;
	m=read();
	for(int i=1;i<=m;++i) {
		int x=getfa(read()),y=getfa(read());
		fa[min(x,y)]=max(x,y);
	}
	cout<<(Sov(b)-Sov(a)+mod)%mod;
	return 0;
}

这道题目确实可做,其中奇怪的枚举前缀的思路是非常有趣的。


T 2 T2 T2

在这里插入图片描述
在这里插入图片描述
“这是一道披着期望皮的容斥题”——Achen
发现n的范围巨大,由于m也不小所以矩阵快速幂宣告GG。
所以直觉(数据范围)告诉我们一定会有与m有关的多项式算法。
先感受一下简单的 O ( m 2 ) O(m^2) O(m2)算法。


首先,期望可以由概率*贡献累加得到。
考虑使第i个点成为最小的白球。
其充要条件是:
1、第i个球是白球
2、前i-1个球全部是黑球
第i个球是白色的概率很好算, ( m − 1 m ) n \left ( \frac{m-1}{m} \right )^n (mm1)n
但是这时候前i-1个球颜色是随意的。
考虑容斥,先减掉至少有一个白球的情况数: C ( i − 1 , 1 ) ∗ ( m − 2 m ) n C(i-1,1)*\left ( \frac{m-2}{m} \right )^n C(i1,1)(mm2)n
显然要再加上至少有两个白球的情况数: C ( i − 1 , 2 ) ∗ ( m − 3 m ) n C(i-1,2)*\left ( \frac{m-3}{m} \right )^n C(i1,2)(mm3)n
最后算出答案还要加上当前点的贡献 i i i
最后公式大概是: ∑ i = 1 m + 1 ∑ j = 0 i − 1 ( − 1 ) j ( i − 1 j ) ( m − j − 1 m ) n \displaystyle\sum_{i=1}^{m+1}\sum_{j=0}^{i-1} (-1)^j\binom{i-1}{j}\left(\frac{m-j-1}{m}\right)^n i=1m+1j=0i1(1)j(ji1)(mmj1)n
乘上答案的 m n m^n mn就是: A n s = ∑ i = 1 m + 1 ∑ j = 0 i − 1 ( − 1 ) j ( i − 1 j ) ( m − j − 1 ) n \displaystyle Ans=\sum_{i=1}^{m+1}\sum_{j=0}^{i-1} (-1)^j\binom{i-1}{j}\left(m-j-1\right)^n Ans=i=1m+1j=0i1(1)j(ji1)(mj1)n
考场上就是写的这个玩意儿。
发现并不会化简

for(long long i=1;i<=m;++i) {
	long long temp=Ksm(m-1,n);
	for(long long j=1,f=-1;j<i;++j,f=-f) {
		temp+=f*C(i-1,j)*Ksm(m-j-1,n)%mod;
		temp=(temp+mod)%mod;
	}
	temp=i*temp%mod;
	ans=(ans+temp)%mod;
}
long long temp=Ksm(m,n);
for(long long j=1,f=-1;j<=m;++j,f=-f) {
	temp+=1*f*C(m,j)*Ksm(m-j,n)%mod;
	temp=(temp+mod)%mod;
}
temp=(m+1)*temp%mod;
ans=(ans+temp)%mod;

大概长这样?
50 50 50滚粗。


我们来看一下优秀而的满分算法。
事实上只是状态的定义有所不同。
(以下期望都是在已经乘上 m n m^n mn的前提下进行推导的)
首先令 f ( x ) f(x) f(x) x x x是最小白球编号的期望。
那么上式可化简为 A n s = ∑ i = 1 m + 1 i ∗ f ( i ) \displaystyle Ans=\sum_{i=1}^{m+1}i*f(i) Ans=i=1m+1if(i)
p ( x ) p(x) p(x)为最小白球编号大于 x x x的期望,也就是说前 x x x个球全部为黑球的期望。
计算贡献,有 A n s = ∑ i = 1 m + 1 i ∗ f ( i ) ⇒ A n s = ∑ i = 1 m + 1 ∑ j = 1 i f ( i ) ⇒ A n s = ∑ i = 0 m p ( i ) \displaystyle Ans=\sum_{i=1}^{m+1}i*f(i)\Rightarrow Ans=\sum_{i=1}^{m+1}\sum_{j=1}^{i}f(i)\Rightarrow Ans=\sum_{i=0}^{m}p(i) Ans=i=1m+1if(i)Ans=i=1m+1j=1if(i)Ans=i=0mp(i)
按照 50 50 50分的算法展开 p ( i ) p(i) p(i)
p ( x ) = ∑ i = 0 x ( − 1 ) i ( x i ) ( m − i ) n \displaystyle \large p(x)=\sum_{i=0}^x(-1)^i\binom {x}{i}\left(m-i\right)^n p(x)=i=0x(1)i(ix)(mi)n
⟹ A n s = ∑ i = 0 m ∑ k = 0 i ( − 1 ) k ( i k ) ( m − k ) n \displaystyle \Longrightarrow Ans=\sum_{i=0}^m\sum_{k=0}^i(-1)^k\binom{i}{k}\left(m-k\right)^n Ans=i=0mk=0i(1)k(ki)(mk)n
这个式子仍然是 O ( m 2 ) O(m^2) O(m2)的,但是我们可以改变枚举顺序: A n s = ∑ k = 0 m ( − 1 ) k ( m − k ) n ( ∑ i = k m ( i k ) ) \displaystyle Ans=\sum_{k=0}^m(-1)^k(m-k)^n\left(\sum_{i=k}^m\binom{i}{k}\right) Ans=k=0m(1)k(mk)n(i=km(ki))
后面那一坨可以化成一个组合数
A n s = ∑ k = 0 m ( − 1 ) k ( m − k ) n ( m + 1 k + 1 ) \displaystyle Ans=\sum_{k=0}^m(-1)^k(m-k)^n\binom{m+1}{k+1} Ans=k=0m(1)k(mk)n(k+1m+1)
然后使用快速幂就是 O ( m l o g m ) O(mlogm) O(mlogm)优秀算法
(其实还可以用线性筛代替快速幂做到O(m))。
代码极简

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,m,ans=0;
const int mod=1e9+7;
inline int Ksm(int a,int x) {
	int rec=1ll;
	while(x) {
		if(x&1ll) rec=1ll*rec*a%mod;
		a=1ll*a*a%mod; x>>=1ll;
	} return rec%mod;
}
int fac[1000005],inv[1000005];
inline int C(int n,int m) {
	return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main() {
	cin>>n>>m;
	fac[0]=inv[0]=1;
	for(int i=1;i<=m+1;++i) fac[i]=1ll*fac[i-1]*i%mod;
	inv[m+1]=Ksm(fac[m+1],mod-2);
	for(int i=m;i;--i) inv[i]=1ll*inv[i+1]*(i+1)%mod;
	for(int i=0,f=1;i<=m;++i,f=-f) {
		int temp=f*(1ll*Ksm(m-i,n)*C(m+1,i+1)%mod);
		ans=(ans+temp)%mod;
	}
	ans=(ans+mod)%mod;
	cout<<ans;
	return 0;
}

T 3 T3 T3

emmm
这题 N O I p NOIp NOIp结束之后再看吧
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
听说要用Min_25筛。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值