problem

luogu-P6622

一条道路上从左至右排列着 mmm 个信号站,初始时从左至右依次编号为 1,2,…,m1,2,\dots,m1,2,…,m,相邻信号站之间相隔 111 单位长度。

每个信号站只能往它右侧的任意信号站传输信号(称为普通传递),每单位长度距离需要消耗 111 单位时间。

道路的最左侧有一个控制塔,它在最左侧信号站的左侧,与其相隔 111 单位长度。

控制塔能与任意信号站进行双向信号传递(称为特殊传递),但每单位长度距离需要消耗 kkk 个单位时间。

对于给定的长度为 nnn 的信号传递序列 SSS,传递规则如下:

  1. 共 n−1n-1n−1 次信号传递,第 iii 次信号传递将把信号从 SiS_iSi​ 号信号站传递给 Si+1S_{i+1}Si+1​ 号。
  2. 若 Si+1S_{i+1}Si+1​ 号信号站在 SiS_iSi​ 号右侧,则将使用普通传递方式,从 SiS_iSi​ 号直接传递给 Si+1S_{i+1}Si+1​ 号。
  3. 若 Si+1S_{i+1}Si+1​号信号站在 SiS_iSi​ 号左侧,则将使用特殊传递方式,信号将从 SiS_iSi​ 号传递给控制塔,再由控制塔传递给 Si+1S_{i+1}Si+1​ 号。
  4. 若 Si=Si+1S_i=S_{i+1}Si​=Si+1​,则信号无须传递。

阿基作为大工程师,他能够任意多次交换任意两个信号站的位置,即他能够重排信号站的顺序,这样会使得 SSS 消耗的传递时间改变。

现在阿基想知道,在他重排信号站顺序后,SSS 所消耗的传递时间最小能是多少。

solution

假设坐标在 x,yx,yx,y 的两个信号塔之间有一次 x→yx\rightarrow yx→y 的传递,可以形式化地表示:
{y−xx≤ykx+kyx>y\begin{cases} y-x&&x\le y\\ kx+ky&&x>y \end{cases} {y−xkx+ky​​x≤yx>y​
这是同构的,也就是说对于每个传递的贡献可以拆成两个信号塔的各自贡献。

假设坐标在 yyy 的信号塔,如果有坐标 x→yx\rightarrow yx→y 的传递。

  • 则对于 yyy 所代表的信号塔而言:

    • x≤yx\le yx≤y,产生 111。
    • x>yx>yx>y,产生 kkk。
  • 反之同理,对于 xxx 所代表的信号塔而言:
    • x≤yx\le yx≤y,产生 −1-1−1。
    • x>yx>yx>y,产生 kkk。

则最终代价等于每个点的贡献乘以其坐标再求和。

具体的数学形式推导:

设 cnt(i,j):cnt(i,j):cnt(i,j): 有多少次信号塔 i→ji\rightarrow ji→j 的传递,即 cnt(i,j)=∑k=1n[Sk=i][Sk+1=j]cnt(i,j)=\sum_{k=1}^n[S_k=i][S_{k+1}=j]cnt(i,j)=∑k=1n​[Sk​=i][Sk+1​=j]。

设下标 ppp 处是 idpid_pidp​ 号信号站,推导一下每个下标对答案的贡献。
ans=∑i=1m∑j=i+1mcnt(idi,idj)∗(j−i)+∑i=1m∑j=1i−1cnt(idi,idj)∗(i+j)∗kans=\sum_{i=1}^m\sum_{j=i+1}^mcnt(id_i,id_j)*(j-i)+\sum_{i=1}^m\sum_{j=1}^{i-1}cnt(id_i,id_j)*(i+j)*k ans=i=1∑m​j=i+1∑m​cnt(idi​,idj​)∗(j−i)+i=1∑m​j=1∑i−1​cnt(idi​,idj​)∗(i+j)∗k=∑i=1mi(∑j=i+1m(k∗cnt(idj,idi)−cnt(idi,idj))+∑j=1i−1(k∗cnt(idi,idj)+cnt(idj,idi)))=\sum_{i=1}^mi\bigg(\sum_{j=i+1}^m\Big(k*cnt(id_j,id_i)-cnt(id_i,id_j)\Big)+\sum_{j=1}^{i-1}\Big(k*cnt(id_i,id_j)+cnt(id_j,id_i)\Big)\bigg) =i=1∑m​i(j=i+1∑m​(k∗cnt(idj​,idi​)−cnt(idi​,idj​))+j=1∑i−1​(k∗cnt(idi​,idj​)+cnt(idj​,idi​)))

设 f(s):f(s):f(s): 考虑到 ∣s∣|s|∣s∣ 位(∣s∣:s|s|:s∣s∣:s 二进制下 111 的个数),前 ∣s∣|s|∣s∣ 位的信号站编号集合为 sss 时的最小代价。

那么 ∑j=i+1m(k∗cnt(idj,idi)−cnt(idi,idj))+∑j=1i−1(k∗cnt(idi,idj)+cnt(idj,idi))\sum_{j=i+1}^m\Big(k*cnt(id_j,id_i)-cnt(id_i,id_j)\Big)+\sum_{j=1}^{i-1}\Big(k*cnt(id_i,id_j)+cnt(id_j,id_i)\Big)∑j=i+1m​(k∗cnt(idj​,idi​)−cnt(idi​,idj​))+∑j=1i−1​(k∗cnt(idi​,idj​)+cnt(idj​,idi​)) 形式中的 jjj 其实就是以 ∣s∣|s|∣s∣ 为划分点,分成在 sss 集合内的编号信号塔和不在的信号塔。

于是我们可以预处理这部分的贡献,只需要知道 iii 和集合 sss 即可。

规范化的,令 g(i,s)=∑j∉s∧j≠ik∗cnt(idj,idi)−cnt(idi,idj)+∑j∈sk∗cnt(idi,idj)+cnt(idj,idi)g(i,s)=\sum_{j\not\in s\wedge j\ne i}k*cnt(id_j,id_i)-cnt(id_i,id_j)+\sum_{j\in s}k*cnt(id_i,id_j)+cnt(id_j,id_i)g(i,s)=∑j​∈s∧j​=i​k∗cnt(idj​,idi​)−cnt(idi​,idj​)+∑j∈s​k∗cnt(idi​,idj​)+cnt(idj​,idi​)。

先预处理 g(i,0)g(i,0)g(i,0),然后每次枚举 sss 二进制下的一个 111(随便拎个 lowbitlowbitlowbit 位)即可 O(1)O(1)O(1) 转移。

一个数从 ∉s\not\in s​∈s 到变成 ∈s\in s∈s,先减去原来的代价再加上 ∈s\in s∈s 里面时计算的代价。

时间复杂度 O(2mm)O(2^mm)O(2mm)。

那么 fff 数组的转移就比较简洁了:枚举 sss 二进制下的 111 位置,取较小值。
f(s)=min⁡i∈s{f(s−{i})+∣s∣g(i,s−{i})}f(s)=\min_{i\in s}\Big\{f(s-\{i\})+|s|g(i,s-\{i\})\Big\} f(s)=i∈smin​{f(s−{i})+∣s∣g(i,s−{i})}
到此时空复杂度均为 O(m2m)O(m2^m)O(m2m)。

发现空间起飞了。其实观察数据分布就会发现,mmm 的变化只有 111,对空间的限制明显狠于时间。

也就是说,难得的需要来考虑卡一下空间了。

有各种大力优化空间的妙做法,但是从应试角度,以及出题角度,私以为这种做法以及可以应付了:

转移方程中 g(i,s)g(i,s)g(i,s) ,若 i∈si\in si∈s 则是不合法的,显然不会被用到。

换言之,对于每个 iii,最多只有 2222^{22}222 种状态。

如果只存合法状态,内存就会 /2/2/2,在时间上而言是常数的东西这里就是个大优化了。

考虑怎么减半?

  • 只用让每个 sss 的前 i−1i-1i−1 位不动,(s&(1<<i)-1)
  • 其余位置整体右移一位,相当于 /2/2/2,(s^(s&(1<<i)-1))>>1
  • 最后把两者加起来即可。

code

#include <bits/stdc++.h>
using namespace std;
#define maxn 23
int n, m, k;
int f[1 << maxn], g[maxn][1 << maxn - 1], cnt[maxn][maxn];
int lowbit( int x ) { return x & -x; }
int main() {scanf( "%d %d %d", &n, &m, &k );for( int i = 1, x, lst = -1;i <= n;i ++ ) {scanf( "%d", &x ); x --;if( ~ lst ) ++ cnt[lst][x];lst = x;}for( int i = 0;i < m;i ++ ) {for( int j = 0;j < m;j ++ )if( i ^ j ) g[i][0] += k * cnt[j][i] - cnt[i][j];for( int s = 1;s < (1 << m);s ++ )if( ! (s >> i & 1) ) {int t = s ^ lowbit( s );int j = __builtin_ffs( s ) - 1;g[i][(s & (1<<i)-1) + ((s ^ (s & (1<<i)-1)) >> 1)] = g[i][(t & (1<<i)-1) + ((t ^ (t & (1<<i)-1)) >> 1)] + (k * cnt[i][j] + cnt[j][i]) - (k * cnt[j][i] - cnt[i][j]);}}memset( f, 0x3f, sizeof( f ) ); f[0] = 0;for( int s = 1;s < (1 << m);s ++ ) {int x = __builtin_popcount( s );for( int i = 0;i < m;i ++ )if( s >> i & 1 ) {int t = s ^ (1 << i);f[s] = min( f[s], f[t] + x * g[i][(t & (1<<i)-1) + ((t ^ (t & (1<<i)-1)) >> 1)] );}}printf( "%d\n", f[(1 << m) - 1] );return 0;
}

[省选联考 2020 A/B 卷] 信号传递(状压dp + 卡空间)相关推荐

  1. [省选联考 2020 A/B 卷] 冰火战士(树状数组上二分)

    文章目录 problem solution(10pts) code(10pts) solution(30pts) code(30pts) solution(60pts) code(60pts) sol ...

  2. P6620 [省选联考 2020 A 卷] 组合数问题(斯特林数、下降幂)

    解析 给出 n,x,pn,x,pn,x,p 和一个 mmm 次的多项式 f(k)f(k)f(k),求解: ∑k=0nf(k)xk(nk)modp\sum_{k=0}^nf(k)x^k\binom n ...

  3. P6628-[省选联考 2020 B 卷] 丁香之路【欧拉回路,最小生成树】

    正题 题目链接:https://www.luogu.com.cn/problem/P6628 题目大意 给出nnn个点的一张完全无向图,i∼ji\sim ji∼j的边权是∣i−j∣|i-j|∣i−j∣ ...

  4. 省选联考 2020 A 卷

    省赛虽然炸了,但题解还是要写滴(笑) 第 200 篇 b l o g 祭 ! ! \Huge 第200篇blog祭!! 第200篇blog祭!! 组合数问题 题意简单明了 首先要看出要把多项式的每一项 ...

  5. [省选联考 2020 B 卷] 卡牌游戏 题解c++

    看到这题,真的忒简单啊! 咳咳,开个玩笑.关于这题: 题目描述 轩轩某天想到了一个卡牌游戏,游戏规则如下: 初始时轩轩的手中有自左向右排成一排的 n 张卡牌,每张卡牌上有一个整数分值. 接下来,轩轩每 ...

  6. P7516 [省选联考 2021 A/B 卷] 图函数

    解析 纯纯的人类智慧题. 关键性质:vvv 可以在计算 f(u,G)f(u,G)f(u,G) 时产生贡献,当且仅当 GGG 中 u,vu,vu,v 之间可以通过 [v,n][v,n][v,n] 的点互 ...

  7. P7519-[省选联考 2021 A/B 卷]滚榜【状压dp】

    正题 题目链接:https://www.luogu.com.cn/problem/P7519 题目大意 nnn个队伍,队伍之间按照得分从小到大排名,得分相同的按照编号从小到大排.开始时每个队伍有个初始 ...

  8. 2009成渝微型计算机处于空白,学海园大联考 2020届高三信息卷(二)文综答案

    学海园大联考 2020届高三信息卷(二)文综答案 需要核对本张试卷答案请点击页面底部"立即查看" 更多试卷易对试卷答案核对请微信公众号搜索"答案易对网"关注! ...

  9. 大连大学两日游———2021省选联考游记

    大连大学两日游---2021省选联考游记 今年高一,今年省选就是去积累大赛经验的,为明年的主场做准备.考前参加过几次学校组织的模拟赛(死难死难 ),都得了不点分,所以本来就没多大目标,不爆零就行,但是 ...

最新文章

  1. matlab中sinks,MATLAB Simulink模块库详解(二)Sinks篇
  2. DCIM在数据中心现代化计划中的作用
  3. 幅度和幅值有区别吗_克拉克 (Clark) 变换中等幅值 (2/3) 和等功率 (sqrt(2/3)) 变换的公式推导...
  4. bzoj4330:JSOI2012 爱之项链
  5. 图解Http学习第三章
  6. Python元祖,列表,字典,集合的比较
  7. 利用自定义注解实现权限验证
  8. 9-6 虚拟哈希分布
  9. proFTPd的使用配置
  10. 统计某个字符串出现的次数
  11. 梅特勒托利多xk3124电子秤说明书_梅特勒电子秤校准(标定)步骤
  12. 【程序35】 ArrayChange.java 题目:输入数组,最大的与第一个元素交换,最小的与最后一个元素交换,输出数组。
  13. 【磁盘】 文件外存分配方式
  14. linux上进行base64编码解码
  15. 第三方登入时昵称出现emoji表情致mysql插入失败
  16. MySQL 5.7都即将停只维护了,是时候学习一波MySQL 8了
  17. 小鸟云服务器:网络基本概念服务、协议、进程、端口之间的关系
  18. 【嵌入式底层知识修炼】基于通用消抖算法,拓展通用非阻塞Key按键识别算法
  19. 我这样写python代码表白泡到了我的女神师姐
  20. Mysql数据库基本知识一(表的操作在二中)

热门文章

  1. 备战美赛,这些你应该知道的知识点
  2. php根据分辨率跳转,使用PHP将分辨率转换为Aspect比率
  3. java什么时候可能产生内存溢出_哪些场景会产生OOM?怎么解决?
  4. oracle中sql行数的计算,Oracle技术网—如何利用DBMS_SQL包和游标计算当前用户下所有表的行数...
  5. 计算机专业专业课代号408,计算机专业考研你一定要知道的事情!
  6. linq查询不包含某个值的记录_【翻译】C#表达式中的动态查询
  7. 武汉大学计算机学院2019考研复试,2019年武汉大学硕士研究生复试及录取名单汇总...
  8. linux sudo 必须属于用户ID0,sudo:/usr/bin/sudo 务必属于用户 ID 0(的用户)并且设置 setuid 位...
  9. oracle 查看函数被哪些触发器引用_oracle如何查看存储过程,存储函数,触发器的具体内容...
  10. 数据结构——图-迪杰斯特拉算法