快速幂取模的板子题:
https://www.luogu.com.cn/problem/P1226
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e3;
const int INF=1e12;
double dp[maxn][maxn];
int k;
int fastpow(int a,int n){
int base=a,res=1;
while(n){
if(n&1)res=(res%k*base%k)%k;
base=(base%k*base%k)%k;
n>>=1;
}
return res;
}
signed main(){
int a,n;
cin>>a>>n>>k;
int ll=fastpow(a,n);
cout<<a<<"^"<<n<<" mod "<<k<<"="<<ll%k<<endl;
return 0;
}
/*****************************************************/
学了下概率dp,其中有个问题困扰了我两天。
在期望dp中,有这样的问题(简化):
你现在0点,要走到4号点,你在接下来***1***分钟有
p
0
,
p
1
,
p
2
p0,p1,p2
p0,p1,p2的概率走0步,1步,2步。
问从0走到4花费时间的期望。
在期望dp中,我们以
d
p
i
dpi
dpi表示从
i
i
i到4的期望。
那么,对于
d
p
0
dp0
dp0,我们接下来有三种选择状态:
不走,走1步,走2步。
所以,根据期望的线性关系:
E
(
x
+
y
)
=
E
(
x
)
+
E
(
y
)
E(x+y)=E(x)+E(y)
E(x+y)=E(x)+E(y)得出:
d
p
0
=
d
p
0
+
d
p
1
+
d
p
2
?
dp0=dp0+dp1+dp2?
dp0=dp0+dp1+dp2?或者:
d
p
0
=
d
p
1
+
d
p
2
?
dp0=dp1+dp2?
dp0=dp1+dp2?
好吧,这是完全错误的。
从
d
p
0
dp0
dp0转移到另外三个点是有概率的,我认为实际上仅把所谓的
d
p
i
dpi
dpi当作一个数值就行了。
那么对于dp0,它转移到另外三个数值(包括它本身),其实就是一个概率问题,而这同时消耗了1分钟时间。
E
(
x
+
y
)
=
E
(
x
)
+
E
(
y
)
E(x+y)=E(x)+E(y)
E(x+y)=E(x)+E(y) 这时,我们认为 x+y是最终态,x,y是可以跳转的状态。
d
p
0
=
p
0
(
d
p
0
+
1
)
+
p
1
(
d
p
1
+
1
)
+
p
2
(
d
p
2
+
1
)
dp0=p0(dp0+1)+p1(dp1+1)+p2(dp2+1)
dp0=p0(dp0+1)+p1(dp1+1)+p2(dp2+1)得出:
d
p
0
=
p
0
d
p
0
+
p
1
d
p
1
+
p
2
d
p
2
+
1
dp0=p0dp0+p1dp1+p2dp2+1
dp0=p0dp0+p1dp1+p2dp2+1
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define gcd(x,y) __gcd(x,y)
const int inf=0x3f;
const int INF=0x3f3f3f3f;
const int maxn=5e3+5;
int read() {
int x = 0, w = 1;
char ch = 0;
while (ch < '0' || ch > '9') {if (ch == '-') w = -1;ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}
return x * w;
}
double dp[maxn][maxn];
double p[maxn][maxn][4];
signed main()
{
int r, c;
while(~scanf("%d%d", &r, &c))
{
for(int i=1; i<=r; ++i)
for(int j=1; j<=c; ++j)
for(int k=1; k<=3; ++k)
scanf("%lf", &p[i][j][k]);
dp[r][c] = 0;
for(int i=r; i>0; --i)
for(int j=c; j>0; --j)
{
if(p[i][j][1]==1 || (i==r&&j==c))
continue;
dp[i][j] = (p[i][j][2]*dp[i][j+1] + p[i][j][3]*dp[i+1][j] + 2)/(1-p[i][j][1]);
}
printf("%.3f\n", dp[1][1]);
}
return 0;
}
数论学习第三天
/*********************************************************/
掷色子
题意:一个n面的骰子,求期望掷几次能使得每一面都被掷到。
期望dp的方法在前面已经说过。
现在,
我们再来回顾以下:
假设你已经取了
i
i
i个面,之后你再取一个没有取过的面,概率是:
(
n
−
1
)
/
n
(n-1)/n
(n−1)/n
我们以
E
i
Ei
Ei表示取到
i
i
i个面的期望,
那么
E
[
i
]
=
(
E
[
i
+
1
]
+
1
)
(
n
−
i
)
/
n
+
E
[
i
]
i
/
n
E[i]=(E[i+1]+1)(n-i)/n+E[i]i/n
E[i]=(E[i+1]+1)(n−i)/n+E[i]i/n
根据期望的线性性质,
E
[
n
]
E[n]
E[n]就等于一个求和的数列:???
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define gcd(x,y) __gcd(x,y)
const int inf=0x3f;
const int INF=0x3f3f3f3f;
const int maxn=5e5+5;
int read() {
int x = 0, w = 1;
char ch = 0;
while (ch < '0' || ch > '9') {if (ch == '-') w = -1;ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}
return x * w;
}
double dp[maxn];
signed main()
{
int t;
cin>>t;
while(t--){
double n;
cin>>n;
if(n==1){
cout<<1<<endl;
continue;
}
memset(dp,0,sizeof(dp));
for(int i=n-1;i>=0;--i){
dp[i]=dp[i+1]+(double)n/(n-i);
}
printf("%.2lf\n",dp[0]);
}
return 0;
}
同时,tips:期望是概率的倒数??
假如你每次买彩票有
1
/
n
1/n
1/n 的概率中百万大奖,那么你中一次的期望是买
n
n
n次。
也就是说只要你买的够多,平均买n次就有一次中奖。(冲鸭!!)
通过以上结论,我们再来顺着推:
还是期望的线性性质:
E
(
x
+
y
)
=
E
(
x
)
+
E
(
y
)
E(x+y)=E(x)+E(y)
E(x+y)=E(x)+E(y)
可能也就是这个可恶的性质,让期望dp能立足竞赛领域吧~!
那么,我们现在已经扔骰子扔出了
i
i
i面,现在再扔出一个新面的概率是:
(
n
−
i
)
/
n
(n-i)/n
(n−i)/n 这还需要算吗?
那么,扔出新面的期望是:
n
/
(
n
−
i
)
n/(n-i)
n/(n−i)
根据期望的线性法则:
和的期望等于期望的和
那么,我们只需对这个式子来个从1到
n
−
1
n-1
n−1的求和就
g
a
m
e
.
o
v
e
r
game.over
game.over了。
#include<cstdio>
#include<algorithm>
using namespace std;
int n;
double ans;
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);ans=0;
for (int i=1;i<=n;i++)ans+=(double)1/i;
ans*=n;
printf("%.2lf\n",ans);
}
return 0;
}
这,板子题。
飞行棋 hdu 4405
题意:从1走到n,骰子有6面,要到x的点数就从当前位置i走到i+x。
其中,有如果走到某些点,会直接跳转到某些点。
问从起到走到终点摇骰子次数的期望。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define gcd(x,y) __gcd(x,y)
const int inf=0x3f;
const int INF=0x3f3f3f3f;
const int maxn=5e5+5;
int read() {
int x = 0, w = 1;
char ch = 0;
while (ch < '0' || ch > '9') {if (ch == '-') w = -1;ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}
return x * w;
}
double dp[maxn];
signed main()
{
int n,m;
while(cin>>n>>m&&n+m){
map<int,int>a;
for (int i = 0; i < m; ++i) {
int b,c;
cin>>b>>c;
a[b]=c;
}
for(int i=0;i<maxn;i++)
dp[i]=0;
dp[n]=0;
for(int i=n-1;i>=0;--i){
if(a[i]!=0)
{
dp[i]=dp[a[i]];
}
else dp[i]=1.0*(dp[i+1]+dp[i+2]+dp[i+3]+dp[i+4]+dp[i+5]+dp[i+6])/6+1;
}
printf("%.4lf\n",dp[0]);
}
return 0;
}
************我是邪恶的分割线***************************************
数论学习第四天
组合数 划分数链接
题意 ;给一个数,用比它小的几个数相加它,最多能有多少种组合。
这是用dfs+记忆化做的:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define gcd(x,y) __gcd(x,y)
const int inf=0x3f;
const int INF=0x3f3f3f3f;
const int maxn=5e3+5;
int read() {
int x = 0, w = 1;
char ch = 0;
while (ch < '0' || ch > '9') {if (ch == '-') w = -1;ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}
return x * w;
}
int dp[2001][2001];
int c[maxn][maxn];
int vis[maxn][maxn];
int dfs(int x,int y){
if(x==1||y==1)return 1;
// if(x==y)return 1;
if(x==0)return 1;
int res;
if(dp[x][y])
return dp[x][y];
if(y>x)
y=x;
res= dfs(x,y-1)+dfs(x-y,y);
dp[x][y]=res;
return res;
}
signed main()
{
int n;
memset(dp,0,sizeof(dp));
while(cin>>n){
cout<<dfs(n,n)<<endl;
}
return 0;
}
这是用dp做的:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define gcd(x,y) __gcd(x,y)
const int inf=0x3f;
const int INF=0x3f3f3f3f;
const int maxn=5e3+5;
int read() {
int x = 0, w = 1;
char ch = 0;
while (ch < '0' || ch > '9') {if (ch == '-') w = -1;ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}
return x * w;
}
int dp[2001][2001];
signed main()
{
int n;
for(int i=0;i<2001;i++)
dp[0][i]=1;
n=2000;
dp[1][1]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++){
if(i==1&&j==1)continue;
if(i<j)
dp[i][j]=dp[i][i];
else
dp[i][j]=dp[i][j-1]+dp[i-j][j];
}
}
while(cin>>n){
cout<<dp[n][n]<<endl;
}
return 0;
}
数 论 学 习 第 n 天 数论学习第n天 数论学习第n天
青蛙的约会
题意:
长
L
L
L的圆,青蛙
A
A
A在点
x
x
x,每次能跳m米,青蛙
B
B
B在点
y
y
y,每次能跳n米。跳一次所花时间相同。
问:他们什么时候相遇?
由题意:
设跳
k
k
k次相遇,由题意:
x
+
k
×
m
≡
y
+
k
×
n
(
m
o
d
L
)
x+k \times m\equiv y+k \times n\pmod{L}
x+k×m≡y+k×n(modL)
很明显的同余方程,转化成一元二次:
k
×
(
m
−
n
)
+
s
×
l
=
y
−
x
k\times (m-n)+s\times l=y-x
k×(m−n)+s×l=y−x
此时,当
(
y
−
x
)
÷
g
c
d
(
m
−
n
,
l
)
(y-x)\div gcd(m-n,l)
(y−x)÷gcd(m−n,l)为整数时有解。
然后
e
x
g
c
d
exgcd
exgcd求出解即可。
由于
m
−
n
m-n
m−n只在非负数时
g
c
d
gcd
gcd才有意思,所以需要特判:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,x,y);
int tmp=x;
x=y;
y=tmp-a/b*y;
return d;
}
signed main(){
int x,y,m,n,l,X,Y,gcd;
cin>>x>>y>>m>>n>>l;
int a=n-m,b=l,d=x-y;
if(a<0){ //数据有加强,如果这里没写的话只能拿70
a=-a;
d=-d;
}
if(d%(gcd=exgcd(a,b,X,Y))!=0){
cout<<"Impossible"<<endl;
}else{
int mod=l/gcd;
cout<<(d/gcd*X%mod+mod)%mod<<endl;
}
return 0;
}