Codeforces 1139D(期望dp)

题意是模拟一个循环,一开始有一个空序列,之后每次循环:

1.从1到m中随机选出一个数字添加进去,每个数字被选的概率相同。

2.检查这个序列的gcd是否为1,如果为1则停止,若否则重复1操作直至gcd为1为止。

求这个序列的长度期望。

 

也是花了一晚上学习了一下期望dp。

 

设dp[i]表示当前gcd为i,到gcd为1时添加的元素个数期望。

然后就是传统的期望dp模型了:

 

dp[i]=p[ij]dp[j]+w[ij]

 

此处w为1,因为每次是添加1个元素

初始化状态dp[1]=0,因为当gcd为1的时候已经无法再添加元素

状态转移就是枚举i的因数j,然后计算1到m中有多少个数字x使得gcd(x,i)=j,设个数为tp,另一方面,还要计算有多少个数字y使得gcd(y,i)=i,设个数为z,从而有:

z=m/i(此处除法为向下取整)

dp[i]=z/m*dp[i]+Σ(tp/m*dp[j])+1 (此处的除法为取模意义下的除法,即乘以逆元)

也就是

dp[i]=(Σ(tp/m*dp[j])+1)*m/(m-z) (除法意义同上)

最后,由于起点并未明确确定,此处要手动设定起点,对于每个起点,都有1/m的概率选到,所以答案就是

1+Σdp[i]/m (取模下除法)

 

至于求tp,就是对x/i这个数字质因数分解之后容斥定理求个数,由于本人手残这部分写挂了好几次,终于也是在千辛万苦之后才写对

 

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define ll long long
  4 const ll mod=1e9+7;
  5 ll q_p(ll a,ll b)
  6 {
  7     ll ans=1;
  8     while(b)
  9     {
 10         if(b&1)
 11         {
 12             ans*=a;
 13             ans%=mod;
 14         }
 15         b>>=1;
 16         a*=a;
 17         a%=mod;
 18     }
 19     return ans;
 20 }
 21 ll inv(ll x)
 22 {
 23     return q_p(x,mod-2);
 24 }
 25 
 26 ll ret;
 27 vector<ll>vec;
 28 void dfs(ll idx,ll dep,ll lim,ll num,ll tmp)
 29 {
 30     if(num>100000) return;
 31     if(dep==lim)
 32     {
 33         if(lim%2)
 34             ret+=tmp/num;
 35         else
 36             ret-=tmp/num;
 37         return;
 38     }
 39     if(idx>=vec.size()) return;
 40     dfs(idx+1,dep+1,lim,num*vec[idx],tmp);
 41     dfs(idx+1,dep,lim,num,tmp);
 42 }
 43 
 44 bool vis[100005];
 45 ll calc(ll x,ll k,ll n)
 46 {
 47     ll tmp=n/k;
 48     ll tt=x/k;
 49     for(ll i=2;;i++)
 50     {
 51         while(tt%i==0)
 52         {
 53             if(!vis[i]) vec.push_back(i),vis[i]=1;
 54             tt/=i;
 55         }
 56         if(i>sqrt(tt)) i=tt-1;
 57         if(tt==1) break;
 58     }
 59     ret=0;
 60     for(int i=1;i<=vec.size();i++)
 61         dfs(0,0,i,1,tmp);
 62     for(int i=0;i<vec.size();i++) vis[vec[i]]=0;
 63     vec.clear();
 64     return tmp-ret;
 65 }
 66 
 67 
 68 ll dp[100005];
 69 int main()
 70 {
 71     #ifdef amori
 72     clock_t start = clock();
 73     #endif //amori
 74 
 75     ll m;
 76     cin>>m;
 77     dp[1]=0;
 78     ll invm=inv(m);
 79     for(ll i=2;i<=m;i++)
 80     {
 81         dp[i]=1;
 82         for(ll j=1;j<=sqrt(i);j++)
 83         {
 84             if(i%j==0)
 85             {
 86                 //cout<<i<<" "<<j<<" "<<calc(i,j,m)<<" "<<calc(i,i/j,m)<<endl;
 87                 dp[i]+=dp[j]*invm%mod*calc(i,j,m);
 88                 dp[i]%=mod;
 89                 if(j!=1 && i!=j*j)
 90                 {
 91                     dp[i]+=dp[i/j]*invm%mod*calc(i,i/j,m);
 92                     dp[i]%=mod;
 93                 }
 94             }
 95         }
 96         ll tp=m/i;
 97         dp[i]=dp[i]*m%mod*inv(m-tp);
 98         dp[i]%=mod;
 99     }
100     ll sum=0;
101     for(int i=1;i<=m;i++)
102     {
103         sum+=dp[i];
104         sum%=mod;
105     }
106     cout<<sum*invm%mod+1<<endl;
107 
108     #ifdef amori
109     clock_t end = clock();
110     cout<<"Done in "<<end-start<<"ms"<<endl;
111     #endif // amori
112 }
View Code

 

 

是不是写的很烂,写的很烂就对了

别人构造级数求和一下就过了,本蒟蒻还在搞期望dp,顶不住鸭。

转载于:https://www.cnblogs.com/iamamori/p/10575944.html

区间DP是一种动态规划的方法,用于解决区间范围内的问题。在Codeforces竞赛中,区间DP经常被用于解决一些复杂的字符串或序列相关的问题。 在区间DP中,dp[i][j]表示第一个序列前i个元素和第二个序列前j个元素的最优解。具体的转移方程会根据具体的问题而变化,但是通常会涉及到比较两个序列的元素是否相等,然后根据不同的情况进行状态转移。 对于区间长度为1的情况,可以先进行初始化,然后再通过枚举区间长度和区间左端点,计算出dp[i][j]的值。 以下是一个示例代码,展示了如何使用区间DP来解决一个字符串匹配的问题: #include <cstdio> #include <cstring> #include <string> #include <iostream> #include <algorithm> using namespace std; const int maxn=510; const int inf=0x3f3f3f3f; int n,dp[maxn][maxn]; char s[maxn]; int main() { scanf("%d", &n); scanf("%s", s + 1); for(int i = 1; i <= n; i++) dp[i][i] = 1; for(int i = 1; i <= n; i++) { if(s[i] == s[i - 1]) dp[i][i - 1] = 1; else dp[i][i - 1] = 2; } for(int len = 3; len <= n; len++) { int r; for(int l = 1; l + len - 1 <= n; l++) { r = l + len - 1; dp[l][r] = inf; if(s[l] == s[r]) dp[l][r] = min(dp[l + 1][r], dp[l][r - 1]); else { for(int k = l; k <= r; k++) { dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]); } } } } printf("%d\n", dp[n]); return 0; } 希望这个例子能帮助你理解区间DP的基本思想和应用方法。如果你还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值