模拟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 }