模拟33 题解

A. 春思

比较简单的一道题,甚至是见过的原题。

约数和是积性函数,搞一搞等差数列求和,乘到一起就可以了。

 

 

 

B. 密州盛宴

显然,方案合法的条件是苏轼每轮都能吃到菜。

所以就发现任意后缀0,1个数必须满足,$sufcnt_0-sufcnt_1<=1$。

所以当不满足条件时找到第一个1,把他交换过来,答案对两者之间的距离取max,显然这样能得到一个最优答案。

左侧用单调指针,复杂度$O(n)$,过不去。

然后就发现,直接对$sufcnt0-sufcnt1$取个$max$,然后输出$max-1$就可以了。

这个取max的操作是等价于上述的单调指针算法的。

因为任何交换不影响交换之前的点的$sufcnt$,

一个交换,两点之间的距离一定可以表示为交换的1的$sufcnt_0-sufcnt_1-1$。

 

 

 

 

C. 赤壁情

比较新奇的dp题。

设$dp(i,j,k,0/1/2)$表示已经插入i个数,形成了j段连续的数,这些数构成的权值为k,已经有了0/1/2个数在l或r边界上的方案数。

值得注意的一点是,利用先插入的数小于后插入的数的值的性质,

我们将已经插入的数的贡献直接算在了权值中,而不是构成差时再加入,这样才使状态没有了后效性。

另外,我们在dp的过程中,并不确定每一段的具体位置,只知道它们之间的左右位置关系,和是否接在端点。

在插入了最后一个数,对于合法方案,具体位置才得到确定。

五个转移方程,分别对应:

接在一段旁边,

连接两段,

单独成一段,

放在端点并接上一段,

单独放在端点。

在转移中乘上每个方程对应的系数就可以了。

要求保留30位小数,用高精度。

在作除法之前将答案左移31位(十进制),第31位表示小数点后的第一位。

最后根据最后一位的大小决定是否要进位。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<iomanip>
  5 using namespace std;
  6 int n,m,k;
  7 namespace DOUBLE{
  8     const int N=105;
  9     double dp[2][N*N][N/2][3],ans;
 10     void go(){
 11         int cur=1; const int base=n*(n+1)/2;
 12         dp[cur][base-2][1][0]=1;
 13         dp[cur][base-1][1][1]=2;
 14         dp[cur][base][1][2]=1;
 15         for(int i=2;i<=n;++i){
 16             cur^=1;
 17             const int lm=min(i,n-i+1)+1;
 18             for(int j=0;j<=2*base;++j)
 19                 for(int k=1;k<=lm;++k)
 20                     for(int u=0;u<3;++u) dp[cur][j][k][u]=0;
 21             for(int j=0;j<=2*base;++j)
 22                 for(int k=1;k<=lm;++k)
 23                     for(int u=0;u<3;++u){
 24                         dp[cur][j][k][u]+=dp[cur^1][j][k][u]*(k*2-u);//接在后面
 25                         if(j+2*i<=2*base) dp[cur][j+2*i][k-1][u]+=dp[cur^1][j][k][u]*(k-1);//合并
 26                         if(j-2*i>=0) dp[cur][j-2*i][k+1][u]+=dp[cur^1][j][k][u]*(k+1-u);//单独
 27                         if(j+i<=2*base&&u!=2) dp[cur][j+i][k][u+1]+=dp[cur^1][j][k][u]*(2-u);//接在一端
 28                         if(j-i>=0&&u!=2) dp[cur][j-i][k+1][u+1]+=dp[cur^1][j][k][u]*(2-u);//在一端单独开
 29                     }
 30         }
 31         for(int i=m+base;i<=2*base;++i) ans+=dp[cur][i][1][2];
 32         for(int i=1;i<=n;++i) ans/=i;
 33         cout<<fixed<<setprecision(k)<<ans<<endl;
 34     }
 35 }
 36 namespace BIGINT{
 37     const int N=55;
 38     struct BigInteger{
 39         const static long long base=1e10;
 40         long long s[12];
 41         int num;
 42         void set(int x){
 43             memset(s,0,sizeof(s));
 44             s[num=1]=x;
 45         }
 46         friend void operator +=(BigInteger &a,BigInteger b){
 47             a.num=max(a.num,b.num);
 48             for(int i=1;i<=a.num;++i){
 49                 a.s[i]+=b.s[i];
 50                 if(a.s[i]>=base) a.s[i]-=base,++a.s[i+1];
 51             }
 52             if(a.s[a.num+1]) ++a.num;
 53         }
 54         friend BigInteger operator *(BigInteger a,int x){
 55             int j=0;
 56             for(int i=1;i<=a.num;++i){
 57                 a.s[i]=a.s[i]*x+j;
 58                 j=a.s[i]/base;
 59                 a.s[i]%=base;
 60             }
 61             if(j) a.s[++a.num]=j;
 62             return a;
 63         }
 64         friend void operator /=(BigInteger &a,int x){
 65             for(int i=a.num;i;--i){
 66                 a.s[i-1]+=a.s[i]%x*base;
 67                 a.s[i]/=x;
 68             }
 69             if(!a.s[a.num]) --a.num;
 70         }
 71     };
 72     BigInteger dp[2][N*N][N/2][3],ans;
 73     void go(){
 74         int cur=1; const int base=n*(n+1)/2;
 75         dp[cur][base-2][1][0].set(1);
 76         dp[cur][base-1][1][1].set(2);
 77         dp[cur][base][1][2].set(1);
 78         for(int i=2;i<=n;++i){
 79             cur^=1;
 80             const int lm=min(i,n-i+1)+1;
 81             for(int j=0;j<=2*base;++j)
 82                 for(int k=1;k<=lm+1;++k)
 83                     for(int u=0;u<3;++u) dp[cur][j][k][u].set(0);
 84             for(int j=0;j<=2*base;++j)
 85                 for(int k=1;k<=lm;++k)
 86                     for(int u=0;u<3;++u){
 87                         dp[cur][j][k][u]+=dp[cur^1][j][k][u]*(k*2-u);//接在后面
 88                         if(j+2*i<=2*base) dp[cur][j+2*i][k-1][u]+=dp[cur^1][j][k][u]*(k-1);//合并
 89                         if(j-2*i>=0) dp[cur][j-2*i][k+1][u]+=dp[cur^1][j][k][u]*(k+1-u);//单独
 90                         if(j+i<=2*base&&u!=2) dp[cur][j+i][k][u+1]+=dp[cur^1][j][k][u]*(2-u);//接在一端
 91                         if(j-i>=0&&u!=2) dp[cur][j-i][k+1][u+1]+=dp[cur^1][j][k][u]*(2-u);//在一端单独开
 92                     }
 93         }
 94         for(int i=m+base;i<=2*base;++i) ans+=dp[cur][i][1][2];
 95         for(int i=1;i<=k+10;++i) ans=ans*10;
 96         for(int i=1;i<=n;++i) ans/=i;
 97         if(ans.s[1]>=ans.base/2) ++ans.s[2];
 98         for(int i=2;i<=ans.num;++i)
 99             if(ans.s[i]>=ans.base) ans.s[i]-=ans.base,++ans.s[i+1];
100         printf("%lld.",ans.s[5]);
101         for(int i=4;i>1;--i) printf("%010lld",ans.s[i]);
102         puts("");
103     }
104 }
105 int main(){
106     scanf("%d%d%d",&n,&m,&k);
107     if(k>=10) BIGINT::go();
108     else DOUBLE::go();
109     return 0;
110 }
View Code

 

posted @ 2019-09-03 11:16  skyh  阅读(203)  评论(0编辑  收藏  举报