数论学习中 持续更新。。。快速幂,概率dp,期望dp

快速幂取模的板子题:
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

上面的证明过程也许非常不严谨,但是,我找了一下午博客,竟然没有找到一篇严格证明的文章。 对此我深表痛心疾首,大佬们都跑去水群了,只有小白还在写博客~ 放一道题:

hdu 3853 期望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;
}

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 (n1)/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)(ni)/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 ni/n 这还需要算吗?
那么,扔出新面的期望是: n / ( n − i ) n/(n-i) n/(ni)
根据期望的线性法则:
和的期望等于期望的和
那么,我们只需对这个式子来个从1到 n − 1 n-1 n1的求和就 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×my+k×n(modL)
很明显的同余方程,转化成一元二次:
k × ( m − n ) + s × l = y − x k\times (m-n)+s\times l=y-x k×(mn)+s×l=yx
此时,当 ( y − x ) ÷ g c d ( m − n , l ) (y-x)\div gcd(m-n,l) (yx)÷gcd(mn,l)为整数时有解。
然后 e x g c d exgcd exgcd求出解即可。
由于 m − n m-n mn只在非负数时 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值