从《彩色圆环》一题探讨一类环上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 ...
随机推荐
- 2019-9-27:渗透测试,metasploit-framework初接触
初次利用metasploit漏洞利用框架,入侵获取win7获取权限 目标机系统:windows7,无修复补丁 目标ip地址:192.168.20.131 任务:利用ms17-010,永恒之蓝漏洞,获取 ...
- Stream系列(七)distinct方法使用
EmployeeTestCase.java package com.example.demo; import lombok.Data; import lombok.ToString; import l ...
- Python函数的默认参数的设计【原创】
在Python教程里,针对默认参数,给了一个“重要警告”的例子: def f(a, L=[]): L.append(a) return L print(f(1)) print(f(2)) print( ...
- docker容器访问宿主机的IP——以rocketmq管理工具为例
在宿主机(MacOS系统)上运行了原生的RocketMQ服务,为了方便管理,需要以Docker方式运行RocketMQ的管理工具——rocketmq-console (项目地址:https://git ...
- fastjson 1.2.24-基于JdbcRowSetImpl的PoC构造与分析
前言: 基于fastjson的第一种payload是基于templatesImpl的方式,但是这种方式要求Feature.SupportNonPublicField才能打开非公有属性的反序列化处理,是 ...
- Web渗透测试流程
什么是渗透测试? 渗透测试 (penetration test)并没有一个标准的定义,国外一些安全组织达成共识的通用说法是:渗透测试是通过模拟恶意黑客的攻击方法,来评估计算机网络系统安全的一种评估方法 ...
- 程序员的算法课(11)-KMP算法
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/m0_37609579/article/de ...
- 【Android - IPC】之Serializable和Parcelable序列化
1.序列化的目的 (1)永久的保存对象数据(将对象数据保存到文件或磁盘中): (2)通过序列化操作将对象数据在网络上进行传输(由于网络传输是以字节流的方式对数据进行传输的,因此序列化的目的是将对象数据 ...
- Prometheus 【目录】
正在陆续更新,内容大体包括: rule.标签重置.cAdversior.自动发现(File 自动发现.DNS自动发现.k8s环境自动发现)等... 目录: prometheus[第一篇] Promet ...
- 为NLog自定义LayoutRenderer
长话短说 前文<解剖HttpClientFactory,自由扩展HttpMessageHandler>主要想讲如何扩展HttpMessageHandler, 示例为在每个Http请求中的 ...
