从《彩色圆环》一题探讨一类环上dp的解法
试题来源
2010中国国家集训队命题答辩
问题描述
小A喜欢收集宝物。一天他得到了一个圆环,圆环上有N颗彩色宝石,闪闪发光。小A很爱惜这个圆环,天天把它带在身边。
一天,小A突然发现圆环上宝石的颜色是会变化的。他十分惊讶,仔细观察这个圆环后发现,圆环上宝石的颜色每天变化一次,而且每颗宝石的颜色都等概率地为特定的M种颜色之一。小A发现了这个秘密后,对圆环更是爱不释手,时时刻刻都在研究。
又经过了一段时间,小A发现因为圆环上宝石的颜色不断变化,圆环有时会显得比其他时候更美丽。为了方便比较,小A这样定义圆环的“美观程度”:
设圆环上相同颜色的宝石构成的连续段长度分别为a1, a2, ..., an;
定义圆环的“美观程度” \(R = \prod_{i=1}^{n} a_i\) 。以图一给出的圆环为例,有a1 = 3, a2 = 2, a3 = 1,故R = 6。
现在小A想知道,在上述前提下,圆环的“美观程度”的期望值E(R)是多少。因为如果知道了E(R),他就可以判断每天变化出的新圆环是否比一般情况更美丽。
说明:“美观程度”的期望值即为对每种可能的圆环状态的“美观程度”与其出现概率的乘积进行求和所得的值。输入格式
输入仅有一行,该行给出依次两个正整数N, M,分别表示宝石的个数和宝石在变化时可能变成的颜色种类数。
输出格式
输出应仅有一行,该行给出一个实数E(R),表示圆环的“美观程度”的期望值。
样例输入
3 2
样例输出
2.25
样例输入
200 1
样例输出
200
数据规模和约定
20%的数据满足1 ≤ N, M ≤ 8;
50%的数据满足1 ≤ N, M ≤ 25;
100%的数据满足1 ≤ N ≤ 200, 1 ≤ M ≤10^9。
先看看这篇官方题解的问题\(A\),了解一下经典的圆环染色问题
——《彩色圆环(circle)》命题报告,吴佳俊
题外话:其实还可以更优,用矩阵快速幂可以优化,也可以特征根推出通项公式,这里不展开讨论了
我们从中获取了一种处理环上dp的思路,即增设一维来维护首尾是否相同
先来看链的情况
设\(f[i]\)表示考虑到第\(i\)位时的期望美观度,显然有
\[
f[i]=\sum_{0 \le j < i} f[j]*(i-j)*P[i-j]*(M-1)
\]
其中\(P[i]\)表示连续选\(i\)个相同一种颜色的概率
\[
P[i] = M^{-i}\\
\]
那么现在用圆环染色的思路来试着写环的dp式
\(f[i][0/1]\)表示要决定的序列的前面(0位)已经确定了一种颜色,考虑到该序列第\(i\)位,且要求该位颜色与(1)/不与(0)0位颜色相同时,期望的美观度(许多题解对\(f\)的定义描述并不准确,实际上这里与圆环染色设置的状态有一点不同,就是实际上开头的颜色是不包含在我们要dp的那一段链中的。这关系到后面计算答案的正确性)
\[
f[i][0] = \sum_{0 \le j < i} f[j][0]*(i-j)*P[i-j]*(m-2) + f[j][1]*(i-j)*P[i-j]*(m-1)\\
f[i][1] = \sum_{0 \le j < i} f[j][0]*(i-j)*P[i-j]
\]
考虑如何求答案。考虑将首尾相接的那个颜色块的贡献单独拎出来计算。枚举首尾相接颜色块两端加起来的总长度\(x\),则总共有\(x\)种分割首尾的方案,每种方案有\(M\)个颜色可以选择,每个方案贡献为\(x\),剩下的部分就可以用\(f\)来表示了
\(x=N\)时要特判
\[
Ans = P[N]*N*M + \sum_{1 \le x < N} x*x*P[x]*M*f[n-x][0]
\]
\(O(n^2)\)的代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<queue>
#include<vector>
using namespace std;
typedef long double ldb;
typedef long long ll;
const ll MXN=1005;
ll N,M;
ldb f[MXN][2];
ldb P[MXN];
int main(){
cin>>N>>M;
P[0]=1;for(ll i=1;i<=N;i++) P[i]=P[i-1]/M;
//f数组开两维,实际上是在与位于0位的虚拟颜色斗智斗勇,即f[i][0/1]表示:某一种颜色在序列的最前方(0位),最后一位是否与该颜色相同,美观程度的期望
//这样设状态就给后面的答案计算提供了便捷
f[0][0]=0;f[0][1]=1;//f[0][0]置0,是因为不能让f[i][1]直接从0转移
for(ll i=1;i<=N;i++){
f[i][0]=f[i][1]=0;
for(ll j=0;j<i;j++){//可以从0转移,给了只有一个块转移的机会
f[i][0]+=f[j][0]*(i-j)*P[i-j]*(M-2)
+f[j][1]*(i-j)*P[i-j]*(M-1);
f[i][1]+=f[j][0]*(i-j)*P[i-j];
}
}
ldb ans=N*P[N]*M;
for(ll x=1;x<N;x++)
ans+=x*x*P[x]*M*f[N-x][0];//一个x是贡献,一个x是分割开头和结尾的方式数,f[N-x][0]则充当了中间部分
printf("%.5Lf",ans);
return 0;
}
我们发现推出的dp方程有一部分是与\(j\)无关的。将它们提出来,维护剩下的只与\(j\)有关的前缀和,复杂度即可降至\(O(N)\)
前缀和优化后\(O(n)\)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<queue>
#include<vector>
using namespace std;
typedef long double ldb;
typedef long long ll;
const ll MXN=1000005;
ll N,M;
ldb f[MXN][2];
ldb powM[MXN];//M^i
int main(){
cin>>N>>M;
powM[0]=1;for(ll i=1;i<=N;i++) powM[i]=powM[i-1]*M;
f[0][0]=0;f[0][1]=1;
ldb s_01=0,s_0j=0;
ldb s_11=1,s_1j=0;
for(ll i=1;i<=N;i++){
f[i][0] = s_01*(M-2)*i/powM[i] + s_0j*(M-2)/powM[i]
+ s_11*(M-1)*i/powM[i] + s_1j*(M-1)/powM[i];
f[i][1] = s_01 *i/powM[i] + s_0j /powM[i];
s_01 += f[i][0]*powM[i];
s_0j += f[i][0]*powM[i]*i;
s_11 += f[i][1]*powM[i];
s_1j += f[i][1]*powM[i]*i;
}
ldb ans=N/powM[N]*M;
for(ll x=1;x<N;x++)
ans+=x*x/powM[x]*M*f[N-x][0];
printf("%.5Lf",ans);
return 0;
}
实际上是会炸精度的,懒得管了:p
从《彩色圆环》一题探讨一类环上dp的解法的更多相关文章
- tyvj 1342 教主泡嫦娥 环上DP
342 教主泡嫦娥 时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 2012年12月21日下午3点14分35秒,全世界各国的总统以及领导人都已经汇聚在中国的方 ...
- [期望DP][纪中]【2010集训队出题】彩色圆环
彩色圆环 感谢名单 十分感谢 JA_Ma 为我讲解了 \(T1\) 的 期望DP 的思想和推论. 十分感谢 SSL_LYF 为我解答了 \(T1\) 的 期望DP 的概率的大小问题. 十分感谢 SSL ...
- 关于一类容斥原理设计 dp 状态的探讨
写在前面 为什么要写?因为自己学不明白希望日后能掌握. 大体思路大概是 设计一个容斥的方案,并使其贡献可以便于计算. 得出 dp 状态,然后优化以得出答案. 下列所有类似 \([l,r]\) 这样的都 ...
- [提升性选讲] 树形DP进阶:一类非线性的树形DP问题(例题 BZOJ4403 BZOJ3167)
转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/7337179.html 树形DP是一种在树上进行的DP相对比较难的DP题型.由于状态的定义多种多样,因此解法也五 ...
- [Hdu-5155] Harry And Magic Box[思维题+容斥,计数Dp]
Online Judge:Hdu5155 Label:思维题+容斥,计数Dp 题面: 题目描述 给定一个大小为\(N*M\)的神奇盒子,里面每行每列都至少有一个钻石,问可行的排列方案数.由于答案较大, ...
- Slope Trick:解决一类凸代价函数DP优化
[前言] 在补Codeforce的DP时遇到一个比较新颖的题,然后在知乎上刚好 hycc 桑也写了这道题的相关题解,这里是作为学习并引用博客的部分内容 这道题追根溯源发现2016年这个算法已经在API ...
- Codeforces Round #369 (Div. 2) D. Directed Roads dfs求某个联通块的在环上的点的数量
D. Directed Roads ZS the Coder and Chris the Baboon has explored Udayland for quite some time. The ...
- HDU 4443 带环树形dp
思路:如果只有一棵树这个问题很好解决,dp一次,然后再dfs一次往下压求答案就好啦,带环的话,考虑到环上的点不是 很多,可以暴力处理出环上的信息,然后最后一次dfs往下压求答案就好啦.细节比较多. # ...
- LightOJ 1074 Extended Traffic(spfa+dfs标记负环上的点)
题目链接:https://cn.vjudge.net/contest/189021#problem/O 题目大意:有n个站点,每个站点都有一个busyness,从站点A到站点B的花费为(busynes ...
随机推荐
- Win10如何快速截屏
Win10不用QQ,如何快速截屏? 年轻的时候想截图总是需要把QQ打开,但是直到我遇到了一种尴尬的场景:就是需要我把鼠标放着标签上,才会有下一步内容出现,这就很难搞. 经过查找资料,做出一些总结. 第 ...
- c#关于数据和方法在不同类中的引用-xdd
关于数据和方法在不同类中的引用 using System; using System.Collections.Generic; using System.Linq; using System.Text ...
- JVM学习笔记(1)--运行时数据区域
运行时数据区域 相对于c,c++.程序设计时,java并不需要手动释放或者创建内存用于存放程序,这的确使得java开发变得容易和轻松,一旦有一天出现了内存泄漏或者内存溢出的问题,如果不了解JVM虚拟机 ...
- PHP+Redis实现延迟任务,实现自动取消与完成订单
简单定时任务解决方案:使用redis的keyspace notifications(键失效后通知事件) : (A)业务场景: 1.当一个业务触发以后需要启动一个定时任务,在指定时间内再去执行一个任务( ...
- Redis系列(四):Redis持久化和主从复制原理
一.持久化 所谓的持久化就是把内存中的数据写到磁盘中去,防止服务宕机后内存数据丢失.Redis4.0之前提供了两种持久化方式:RDB(默认) 和AOF,Redis4.x之后新增了一种混合持久化(本文所 ...
- PHP如何获取视频总时长与码率等信息
利用PHP中的FFmpeg读取视频播放时长与码率等信息 function getVideoInfo($file) { define('FFMPEG_PATH', '/usr/local/ff ...
- input 输入框 只能输入数字、字母、汉字等
1.文本框只能输入数字代码(小数点也不能输入) <input onkeyup="this.value=this.value.replace(/\D/g,'')" onafte ...
- 【LiteOS】STM32F103-LiteOS移植教程(详细篇)【华为云技术分享】
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...
- 使用app测试Modelarts在线服务
1. 基础准备 本demo代码已上传github地址为 https://github.com/zxzxzxygithub/hwmodelartdemo clone下来之后导入android studi ...
- Docker数据挂载
Docker数据管理 在容器中管理数据主要有两种方式: 数据卷(Volumes) 挂载主机目录(Bind mounts) 数据卷 数据卷是一个可供一个或多个容器使用的特殊目录,它绕过UFS,可以提供很 ...