20221109模拟赛题解
A
分析
分类讨论。
对于 \(2\leq i\leq n\),
- 若 \(a_i=a_{i-1}\),有 \(\dfrac{1}{a_i}\) 的概率选对。
- 若 \(a_i>a_{i-1}\),有 \(\dfrac{a_{i-1}}{a_i}\cdot \dfrac{1}{a_{i-1}}=\dfrac{1}{a_i}\) 的概率选对。
- 若 \(a_i< a_{i-1}\),有 \(\dfrac{a_i}{a_{i-1}}\cdot \dfrac{1}{a_i}=\dfrac{1}{a_{i-1}}\) 的概率选对。
最后特殊处理一下 \(1\) 然后求个和即可。
核心代码
const int MAXN=1e7+7;
int n,A,B,C,a[MAXN];double ans;
int main(){
scanf("%d%d%d%d%d", &n, &A, &B, &C, a + 1);
for (int i = 2; i <= n; i++)
a[i] = ((long long) a[i - 1] * A + B) % 100000001;
for (int i = 1; i <= n; i++)
a[i] = a[i] % C + 1;
ans+=1.0/qmax(a[n],a[1]);
for(int i=2;i<=n;i++){
ans+=1.0/qmax(a[i-1],a[i]);
}printf("%.3lf\n",ans);
return 0;
}
B
分析
弱化版是北大羟基的一道题。
有结论:
若 \(f(x)\) 在 \(\R\) 上单调递增,设 \(M=\{x|f(x)=x\}\),\(N=\{x|f^{[n]}(x)=x\}\),则 \(M=N\)。
证明:
设 \(S=\{x|f(x)=x\},T=\{x|f(f(x))=x\}\),用反证法容易证明 \(S\subseteq T\)。
考虑证明 \(S=T\),即证 \(T\subseteq S\),即证 \(\forall x=f(f(x)),x=f(x)\)。
继续用反证法,假设 \(\exist x=f(f(x)),x>f(x)\),由于 \(f(x)\) 是增函数,所以
,矛盾,假设不成立,\(x<f(x)\) 的情形同理。迭代次数更多时也可以这样证明。
观察题目给定的函数一定在 \(\R\) 上单调递增,利用上面的结论可将问题就转化成求 \(f(x)-x=0\) 的解。
观察发现 \(g(x)=f(x)-x\) 恒过 \((0,0)\),且在 \((-\infin,0)\) 有单峰,在 \((0,+\infin)\) 有单谷,三分找到峰点谷点后二分找零点即可。
核心代码
int n,m;
inline double f(double x){
double res=0;
for(int i=1;i<=2*m-1;i+=2) res+=pow(x,i);res/=(x+10);
return res-x;
}int main(){
qread(n,m);if(m==1) return puts("inf"),0;
puts("3");double l=-10,r=0,mid,lmid,rmid;
while(r-l>eps){
mid=(l+r)*0.5;lmid=mid-eps;rmid=mid+eps;
if(f(lmid)>f(mid)) r=mid;else l=mid;
}r=l,l=-10;
while(r-l>eps){
mid=(l+r)*0.5;
if(f(mid)>=eps) r=mid;else l=mid;
}printf("%.6lf ",l);l=0;r=10;
while(r-l>eps){
mid=(l+r)*0.5;lmid=mid-eps;rmid=mid+eps;
if(f(lmid)<f(mid)) r=mid;else l=mid;
}r=10;
while(r-l>eps){
mid=(l+r)*0.5;
if(f(mid)>=eps) r=mid;else l=mid;
}printf("0 %.6lf\n",l);
return 0;
}
C
分析
首先考虑如何判断一个子段合法,这是个经典问题,\(dp_{i,0/1}\) 表示枚举到 \(i\),\(i\) 属于下降/上升子序列,上升/下降子序列的最小/最大值,直接转移即可。
此时枚举每一个子段,判断是否合法,时间复杂度 \(\mathcal O(n^3)\),期望得分 \(30pts\)。
继续观察,发现若 \([l,r]\) 合法,那么 \([i,j](l\leq i\leq j\leq r)\) 一定合法,因为一个合法的子段删掉 LIS 后一定单调下降,而更小的子段就相当于删掉 LIS 中的一些元素,在删掉去掉 LIS 后剩下的一部分元素,一定合法。
于是可以分治地做,记录 \(f_i\) 表示以 \(i\) 开头的合法子段的最靠后的结尾,每次如果 \(f_l=f_r\) 就把整段赋成 \(f_l\),否则就计算 \(mid\) 的 \(f\),再计算左半边和右半边。最后答案是 \(\sum f_i-i+1\)。
核心代码
int n,a[MAXN],dp[MAXN][2],f[MAXN];
void cal(int x){
dp[x][0]=0;dp[x][1]=n+1;
for(int i=x+1;i<=n;i++){
dp[i][0]=-1;if(a[i]<a[i-1]) dp[i][0]=dp[i-1][0];
if(dp[i-1][1]!=-1&&dp[i-1][1]>a[i]) if(dp[i][0]==-1||dp[i][0]>a[i-1]) dp[i][0]=a[i-1];
dp[i][1]=-1;if(a[i]>a[i-1]) dp[i][1]=dp[i-1][1];
if(dp[i-1][0]!=-1&&dp[i-1][0]<a[i]) if(dp[i][1]==-1||dp[i][1]<a[i-1]) dp[i][1]=a[i-1];
if(dp[i][0]==-1&&dp[i][1]==-1) return f[x]=i-1,void();
}f[x]=n;
}void solve(int l,int r){
if(l+1>=r) return;
if(f[l]==f[r]){for(int i=l;i<=r;i++) f[i]=f[l];return;}
int mid=(l+r)>>1;cal(mid);solve(l,mid);solve(mid,r);
}signed main(){
qread(n);int i,j;for(i=1;i<=n;i++) qread(a[i]);cal(1);cal(n);int ans=0;solve(1,n);
for(i=1;i<=n;i++) ans+=f[i]-i+1;printf("%lld\n",ans);
return 0;
}
D
分析
根据库默尔定理,\(n+m\choose m\) 所含 \(p\) 的幂次数 \(p\) 进制下 \(n+m\) 的进位数。
证明:
设 \(vp(n)\) 为 \(n\) 含 \(p\) 的次数,则
,也就是 \(p\) 进制下 \(n+m\) 的进位数,然后就转化成了求满足 \(0\leq n+k\leq A\) 且 \(n+k\) 进位数 $ \geq \alpha$ 的方案数。
定义状态 \(dp_{i,j,0/1,0/1}\) 表示前 \(i\) 位,共进 \(j\) 次,取满/没取满,前一位没进位/进位的方案数。大分类讨论转移即可。