150分
集合均值
【题目描述】
\(3s,512MB\)
有两个可重集合\(A,B\) ,初始时 \(A\) 只包含一个 \(0\) ,\(B\) 是给定的。
执行以下操作:
- 在\(B\) 中随机选一个数 \(y\) ,把 \(y\) 从\(B\) 移动到 \(A\)。
- 给答案加上\(A\) 的平均值。
- 若 \(B\) 非空,回到步骤 \(1\) 。
求最后答案的期望。
【输入格式】
第一行一个整数 \(n,m\) ,表示集合\(B\) 可以分成 \(m\) 个大小为 \(n\) 的部分。
接下来一行 \(n\)个数表示一个部分,每个部分的数相同。
【输出格式】
一行一个整数表示答案模 \(998244353\) 的结果。
【样例 \(1\) 输入】
2 1 1 2
【样例 \(1\) 输出】
249561090
【样例 \(2\) 输入】
3 3 1 2 3
【样例 \(2\) 输出】
346216508
【样例解释】
样例一说明:
有两种可能,答案的期望为\(\frac 1 2(\frac 3 2+2)=\frac 7 4\)。
样例二说明:
该样例中集合 \(B\) 为 \(\{1,2,3,1,2,3,1,2,3\}\) 。
【数据范围】
对于 \(40\%\) 的数据:\(n=1\) 。
对于 \(70\%\) 的数据:\(n \times m \le 10^5\) 。
对于所有数据:\(n \times m \le 2\times 10^7,1 \le n \le 10^5, 1 \le m \le 2\times 10^7\)集合中的数是\([0,10^9]\) 内的整数。
【问题分析】
\(40\) 分:
\(n=1\) ,意味着 \(m\) 个大小为 \(1\) 的相同部分,即 \(m\) 个相同的数。
设该数为 \(a\) ,取出每一个数直到取完总共有 \(A_m^m\) 种方案,每一种方案的概率为 \(\prod_{i=m}^1 \frac 1 i=\frac 1 m\times \frac 1 {m-1}\times ...\times \frac 1 2\) ,由于 \(m\) 个数相同,每种方案最后的答案均相同,为 \(\sum_{j=2}^{m+1}\frac {(j-1)a} j=\frac a 2+\frac {2a} 3+...+\frac {ma} {m+1}\) 。
因此最后的答案 \(ans=A_m^m\times \prod_{i=m}^1\times \sum_{j=2}^{m+1}\frac {(j-1)a} j\) = \(\sum_{j=2}^{m+1}\frac {(j-1)a} j\) 。
设模数为 \(M\), 如果分别计算 \(\frac a 2\%M+\frac {2a} 3\%M+...+\frac {ma} {m+1}\%M\) ,时间复杂度为 \(O(m\log M)\) ,会超时。
需要使用线性求逆元(https://www.cnblogs.com/strve/p/13787783.html),时间复杂度为 \(O(m)\) 。
【\(40\) 分参考程序】
//超时
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M=998244353;
const int N=100100;
int n,m;
ll a[N];
ll ksm(ll t,int q)
{
ll tmp=1;
while(q)
{
if(q&1) tmp=(tmp*t)%M;
q>>=1;
t=(t*t)%M;
}
return tmp;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
if(n==1)
{
ll ans=0;
for(ll i=2;i<=m+1;i++)
ans=(ans+(((i-1)*a[1])%M)*ksm(i,M-2))%M;//费马小定理求逆元
printf("%lld\n",ans);
}
return 0;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M=998244353;
const int N=100100;
int n,m;
ll a[N],inv[200*N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
if(n==1)
{
inv[1]=1;
for(int i=2;i<=m+1;i++) //线性求逆元
inv[i]=((M-M/i)*inv[M%i])%M;
ll ans=0;
for(ll i=2;i<=m+1;i++)
ans=(ans+(((i-1)*a[1])%M)*inv[i])%M;
printf("%lld\n",ans);
}
return 0;
}
聚烷撑乙二醇
【题目描述】
\(2s,512Mb\)
鲁迅曾经说过,要多去尝试,才能最终发现最优的答案。
鲁迅也还说过,要珍惜当下,把握住眼前的机会。
有 \(n\)个随机数生成器,第 \(i\)个生成器可以均匀随机地生成 \([L_i,R_i]\)内的一个实数。
现在你要玩个游戏,从第 \(1\)个生成器到第\(n\) 个生成器,每次当前生成器会生成一个数,你需要选择:
- 相信鲁迅,拿走这个数,游戏结束。
- 相信鲁迅,放弃这个数和这个生成器,使用下一个生成器(前提是下一个生成器必须存在)。
求使用使得期望答案最大的策略时,期望答案是多少。
【输入格式】
第一行一个整数\(n\) 。
接下来 \(n\)行每行两个非负整数表示\(L_i,R_i\) 。
【输出格式】
一行一个保留恰好 $5 $位小数(四舍五入)的浮点数表示答案。
【样例 \(1\) 输入】
2 0 2 1 1
【样例 \(1\) 输出】
1.25000
【样例 \(2\) 输入】
3 0 9 0 9 0 9
【样例 \(1\) 输出】
6.25781
【样例 \(1\) 解释】
如果第一个生成器生成了比 \(1\)小的数就使用下一个,否则拿走,答案的期望为\(5/4\) 。
【数据范围】
对于 \(30\%\) 的数据:\(L_i = R_i\) 。
对于另外 \(40\%\) 的数据:所有 \(L_i\) 都为 \(0\) 且所有 \(R_i\) 都相等。
对于所有数据:\(1\le n \le 10^6, 0 \le L_i \le R_i \le 10^9\) 。
【问题分析】
\(30\) 分:
\(L_i=R_i\) ,不存在概率选择,只选择最大的即可。
【\(30\) 分程序】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000010;
int n,L[N],R[N];
int main()
{
scanf("%d",&n);
int flag=1,ans=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&L[i],&R[i]);
if(L[i]!=R[i]) flag=0;
ans=max(ans,L[i]);
}
if(flag) {printf("%.5lf",ans*1.0);return 0;}
return 0;
}
技术情报局
【题目描述】
\(3s,2048MB\)
有这样一道简单题:给定一个序列求所有区间的最大值的和。
还有这样一道简单题:给定一个序列求所有区间的乘积的和。
众所周知:简单题 + 简单题 = 简单题。
所以,给定一个长为\(n\) 的正整数序列,定义一个区间的权值是该区间的数的乘积乘上该区间的最大值,求该序列所有区间的权值和,答案对\(p\) 取模。
【输入格式】
为了避免输入文件过大,输入仅有一行五个正整数:\(n,s,l,r,p\) 。
然后通过调用如下函数
get(n, s, l, r)得到一个长为\(n\) 的序列,序列的每个数都是\([l,r]\) 范围内的整数:#include <vector> namespace GenHelper { unsigned z1, z2, z3, z4, b; unsigned rand_() { b = ((z1 << 6) ^ z1) >> 13; z1 = ((z1 & 4294967294U) << 18) ^ b; b = ((z2 << 2) ^ z2) >> 27; z2 = ((z2 & 4294967288U) << 2) ^ b; b = ((z3 << 13) ^ z3) >> 21; z3 = ((z3 & 4294967280U) << 7) ^ b; b = ((z4 << 3) ^ z4) >> 12; z4 = ((z4 & 4294967168U) << 13) ^ b; return (z1 ^ z2 ^ z3 ^ z4); } } // namespace GenHelper std::vector<int> get (int n, unsigned s, int l, int r) { std::vector<int> a; using namespace GenHelper; z1 = s; z2 = unsigned((~s) ^ 0x233333333U); z3 = unsigned(s ^ 0x1234598766U); z4 = (~s) + 51; for (int i = 1; i <= n; i ++) { int x = rand_() & 32767; int y = rand_() & 32767; a.push_back(l + (x * 32768 + y) % (r - l + 1)); } return a; }
【输出格式】
一行一个正整数表示答案模 \(p\) 的结果。
【样例 \(1\) 输入】
3 233 1 5 1000
【样例 \(1\) 输出】
256
【样例 \(2\) 输入】
1000 2333 1 1000 998244353
【样例 \(2\) 输出】
627435628
【样例 \(3\) 输入】
10000000 666 1 1000 998244353
【样例 \(3\) 输出】
123415035
【样例 \(1\) 解释】
该样例的序列为 \({3,5,2}\) 。
【数据范围】
对于 \(20\%\) 的数据:\(n \le 500\) 。
对于 \(40\%\) 的数据:\(n \le 5000\) 。
对于 \(60\%\) 的数据:\(n \le 10^6\) 。
对于另外 \(20\%\) 的数据: \(l=r\) 。
对于所有数据:\(1 \le n \le 10^7, 0 \le s \le 2^{30}, 1 \le l \le r \le 10^9, 1 \le p \le 10^9+9\) 。
【问题分析】
\(20\) 分:
暴力枚举每一个区间,计算每一个区间的最大值和乘积。时间复杂度为 \(O(n^3)\) 。
\(40\) 分:
枚举每一个区间,用递推的方式求解区间最大值和乘积,已知区间 \([x,y]\) 的最大值 Max,区间乘积 \(S\),则区间\([x,y+1]\) 的最大值就是 \(max(Max,a[y+1])\) ,区间乘积为 \(S\times a[y+1]\) 。时间复杂度为 \(O(n^2)\) 。
\(60\) 分:
在 \(40\) 分的基础上,其中 \(l=r\) ,则区间所有数都是相同的,只需要枚举区间长度即可,区间最大值,区间乘积都可以 \(O(1)\) 得出,时间复杂度为 \(O(n)\)。
肯德基
【题目描述】
\(3s,512MB\)
定义\(f(x) = \mu (x)^2x\) ,求:\(\sum_{i=1}^nf(i)\) 。
\(\mu\)是莫比乌斯函数,\(\mu(1)=1\) ,\(x>1\) 时 \(\mu(x)\)有值当且仅当\(x\) 可以表示为 \(k\)个互不相等的质数的乘积,此时 \(\mu(x)\)的值为\((-1)^k\) 。
【输入格式】
多组数据,第一行一个整数\(T\) 表示数据组数。
接下来\(T\) 行每行一个整数\(n\) 。
【输出格式】
\(T\)行,每行表示该组数据的答案,鉴于答案可能很大,对\(2^{64}\) 取模 。
【样例 \(1\) 输入】
3 10 100 1000
【样例 \(1\) 输出】
34 2967 303076
【数据范围】
【问题分析】
\(20\) 分:
使用埃式筛分预处理出 \(f(x)\) 的前缀和,时间复杂度接近 \(O(n)\) 。对于每次询问,在 \(O(1)\) 的时间回答。
【\(20\) 分参考程序】
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int N=50010;
int n,T,mob[N],v[N];//定义数组mob[N]保存莫比乌斯函数,v[N]标记是否为质数
ull sum[N];
int main()
{
for(int i=1;i<=N;i++) //埃式筛法求1~N的莫比乌斯函数
mob[i]=1,v[i]=0;
for(int i=2;i<=N;i++)
{
if(v[i]) continue; //被质数筛过,为合数
mob[i]=-1; //i为质数
for(int j=2*i;j<=N;j+=i)
{
v[j]=i;
if((j/i)%i==0) mob[j]=0;
else mob[j]=mob[j]*-1;
}
}
for(int i=1;i<=N;i++)
sum[i]=sum[i-1]+mob[i]*mob[i]*i;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
printf("%lld\n",sum[n]);
}
return 0;
}


浙公网安备 33010602011771号