P10792 『SpOI - R1』笑起来最帅的小孩 题解
Yang18630303 · · 题解
题目传送门
本题,我感觉,给成黄题有点不对,逆元 + 类快速幂 + 数学(期望,概率)应该是绿。
观前提示
所有部分代码都有
#include <iostream>
#include <cmath>
#define int long long
using namespace std;
const int mod=2007072007;
int x,y;
int Mod(int n){
return (n%mod+mod)%mod;
}
本题解话可能语无伦次,请见谅。
Tips 您的正确代码炸了的可能原因
- 十年 OI 一场空,不开
long long见祖宗 - 多测不清空,爆零两行泪
- 逆元炸了
- 快速幂炸了
简述题意
给定一个数字串,和一个空的序列,将数字串中的数字依次等概率的插入序列中两个数字的间隔或者序列首和序列尾,求最终序列的数排在一起形成的数的期望模
期望
何为期望?
举几个例子,如果在一个事件中,结果出现
概率推导
现在,我们知道想求出结果,就要求出出现各种结果的概率,那就求。
首先,我们假设所有
有问题,在刚才的证明中我们假设所有数字均不相同,那么如果有相同的数字呢?我们可以将相同的数字标号区分,比如当输入的数字是
此时,我们就要求最终的期望了,设最终答案为
其中
这样,想求出最终结果
很容易地发现
这部分的代码:
int k;
cin>>k;
int a,l;
int suma=0,n=0,o=0,ans;
for(int i=0;i<k;i++){
cin>>a>>l;
n+=l;
suma+=a*l;
n%=mod;
suma%=mod;
}
逆元
刚才我们分析后发现,有一个子问题是
这里的
如何求
这里我偷懒,贴一个大佬的博客,谢谢。本题中我采用了拓欧的方法。
部分代码
void Eg(int a,int b,int &x,int &y){
if(b==0){
x=1,y=0;
}
else{
Eg(b,a%b,y,x);
y-=a/b*x;
}
}
int ny(int a){
Eg(a,mod,x,y);
return (x%mod+mod)%mod;
}
这样我们只需要求
类快速幂
怎么求
可以用等比数列求和但是别的大佬讲了,我讲一下自己的方法吧
很容易发现一个
long long s_s1(int n){
int o=0;
for(int i=0;i<n;i++){
o=o*10+1;
o%=mod;
}
return o;
}
然而,这道题的
观察特点,发现
然后我们再用快速幂去求
注意这里不能用 pow,会炸 long long,要自己再手打快速幂并取模。
部分代码:
int f_s(int n){//求10^n
if(n==0)return 1;
if(n==1)return 10;
int r=f_s(n/2);
if(n&1){
return Mod(Mod(r*r)*10);
}
else{
return Mod(r*r);
}
}
long long f_s1(int n){//求q(n)
if(n==1)return 1;
if(n==2)return 11;
long long r=f_s1(n/2);
if(n&1){
return Mod(Mod(r*Mod(f_s(n/2))+r)*10+1);
}
else{
return Mod(r*Mod(f_s(n/2))+r);
}
}
复杂度
Code
于是我们就可以贴上完整代码了
注:完整代码中的 mod 即为模数 m。
#include <iostream>
#include <cmath>
#define int long long
using namespace std;
const int mod=2007072007;
int x,y;
int Mod(int n){
return (n%mod+mod)%mod;
}
void Eg(int a,int b,int &x,int &y){
if(b==0){
x=1,y=0;
}
else{
Eg(b,a%b,y,x);
y-=a/b*x;
}
}
int ny(int a){
Eg(a,mod,x,y);
return (x%mod+mod)%mod;
}
int f_s(int n){//求10^n
if(n==0)return 1;
if(n==1)return 10;
int r=f_s(n/2);
if(n&1){
return Mod(Mod(r*r)*10);
}
else{
return Mod(r*r);
}
}
long long f_s1(int n){//求q(n)
if(n==1)return 1;
if(n==2)return 11;
long long r=f_s1(n/2);
if(n&1){
return Mod(Mod(r*Mod(f_s(n/2))+r)*10+1);
}
else{
return Mod(r*Mod(f_s(n/2))+r);
}
}
long long s_s1(int n){
int o=0;
for(int i=0;i<n;i++){
o=o*10+1;
o%=mod;
}
return o;
}
void solve(){
int k;
cin>>k;
int a,l;
int suma=0,n=0,o=0,ans;
for(int i=0;i<k;i++){
cin>>a>>l;
n+=l;
suma+=a*l;
n%=mod;
suma%=mod;
}
o=f_s1(n);
n=ny(n);
ans=n*o;
ans%=mod;
ans*=suma;
ans%=mod;
cout<<ans<<endl;
return;
}
signed main(){
int T;
cin>>T;
for(int t=0;t<T;t++){
solve();
}
return 0;
}
最终复杂度
完结撒花!