「刷题笔记」DP优化-状压-EX
棋盘
需要注意的几点:
- 题面编号都是从0开始的,所以第1行实际指的是中间那行
- 对\(2^{32}\)取模,其实就是\(unsigned\ int\),直接自然溢出啥事没有
- 棋子攻击范围不会旋转
首先,我们得找出所有满足条件的同一行的状态,在此之前,我们要先处理出状态\(S\)下,第\(k\)行能被棋子攻击到的格子\(at[k][S]\)
先钦定自己不能攻击自己
再枚举每一个状态中每一个1,然后对攻击范围模板进行左移/右移操作,把这一行这种状态能攻击到的格子或上他
接下来,枚举每一个状态,如果\(at[1][i]\ and\ i==0\),则记录下这种合法的情况。
这个时候我们就可以枚举每一行的状态来求解了,但是这样时间复杂度是\(O(n\cdot 2^{2m})\)的,而在\(n\)给到\(1e6\)的情况下无疑会炸掉,需要考虑进一步的优化
(颓了一下题解)
其实,每一种状态都是由之前的某种状态转移来的,且可以重复,那就说明如果两个状态可以排在一起,他们就可以多次排在一起
考虑矩阵快速幂优化
设初始矩阵中\(A[i][j]=1\)表示\(j\)可以排在\(i\)的后面
则有这样的柿子:\(f[i][j]=f[i][k]\cdot f[k][j]\)
这样的情况下,我们求\(A^n\),因为第一种情况是为空,所以\(\sum\limits_{i=1}^{cnt}A^n[1][i]\)即为答案,意为开始为空,经过\(n\)行后最后一行为状态\(i\)的总数
code:
#include<bits/stdc++.h>
using namespace std;
#define ll unsigned int
#define ull unsigned long long
#define ZZ_zuozhe
#define M 6
#define N 80
ll n,m,p,k;
ll lim[4];
ll at[4][1<<M];
ll sit[N],cnt=0;
struct mat
{
ll a[N][N];
mat(){memset(a,0,sizeof a);}
};
mat operator*(mat a,mat b)
{
mat c;
for(int i=1;i<=cnt;i++)for(int j=1;j<=cnt;j++)for(int k=1;k<=cnt;k++)
{
c.a[i][j]=c.a[i][j]+a.a[i][k]*b.a[k][j];
}
return c;
}
mat ksm(mat a,ll b)
{
mat res=a;
b--;
while(b)
{
if(b&1)res=res*a;
a=a*a;
b>>=1;
}
return res;
}
mat a,ans;
ll t;
int main()
{
scanf("%u%u%u%u",&n,&m,&p,&k);
for(int i=0;i<3;i++)
{
for(int j=0;j<p;j++)
{
scanf("%u",&t);
if(t)lim[i]|=(1<<j);
}
}
lim[1]-=(1<<k);
ll all=(1<<m)-1;
for(int i=0;i<=all;i++)
{
at[0][i]=at[1][i]=at[2][i]=0;
for(int j=0,p=i;p;j++,p>>=1)
{
if((p&1)==0)continue;
at[0][i]|=(j<k)?lim[0]>>(k-j):lim[0]<<(j-k);
at[1][i]|=(j<k)?lim[1]>>(k-j):lim[1]<<(j-k);
at[2][i]|=(j<k)?lim[2]>>(k-j):lim[2]<<(j-k);//处理每种状态每行能够攻击到的位置集合
}
}
for(int i=0;i<=all;i++)
{
if((i&at[1][i])==0)sit[++cnt]=i;
}
for(int i=1;i<=cnt;i++)
{
for(int j=1;j<=cnt;j++)
{
if(((sit[i]&at[0][sit[j]])==0)&&((sit[j]&at[2][sit[i]])==0))a.a[i][j]++;
//处理初始矩阵,如果j放在i后面不冲突就++
}
}
a.a[1][1]=1;
ans=ksm(a,n);
ll aa=0;
for(int i=1;i<=cnt;i++)
{
aa+=ans.a[1][i];
}
printf("%u\n",aa);
return 0;
}
最大前缀和
开始的思路
其实求最大前缀和似乎就是每个负数前判一次……
这几个数总排列方式是\(n!\),答案要乘他就说明直接拿可能值\(\cdot\)这种情况的次数求和就行……
\(n\)的范围看起来挺合适拿来状压的
口胡一下:\(f[i][S]\)为选\(i\)个当前状态为\(S\)的期望和
如果新取一个负数答案应该是直接加,取正数就得更新,但是好像也不一定会更新……
是不是得记录下当前值和更新答案的临界值什么的……
或者用状压记录负数位置……?
(实际证明没一点挨上边)
正解
妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊
状压还能这么出的吗,爱了爱了(虽然做不出来吧……
深挖一下最大前缀和的性质:
\]
设数列\(A\)的最大前缀和为\(pre_j\),即\(\sum\limits_{i=1}^{j} a_i\)
那么由此可以推出:
- 对于任意\(1\leq x \leq j\),有\(\sum\limits_{i=x}^{j} A_i\)定不小于0(否则可以把这部分减去)
- 对于任意\(j+1\leq x \leq n\),有\(\sum\limits_{i=j+1}^{x} A_i\)定不大于0(否则可以把这部分加上)
然后,我们发现,原数列可以分成两部分,分别满足不同的条件,既然如此,我们把满足左边条件的排列个数与满足右边条件的排列个数相乘,就可以得到总方案个数。
但是,题目中要求我们求最大前缀和的期望\(\cdot n!\),也就是用每种可能值乘上能得到它的方案个数,所以,结合状压,我们可以将每种状态满足左右的排列个数相乘,再把每种状态的满足条件方案个数乘上每种状态选的数的总和,即为所求
对于右边,我们在一个所有前缀和为负的数列后面加一个数,若新数列总和仍然为负,则新数列所有前缀和为负
对于左边,我们在一个所有后缀和为正的数列前面加一个数,若新数列总和仍然为非负,则新数列所有后缀和为非负
基于这样的过程,我们可以枚举子集,则有
\]
答案为
\]
另外注意:答案要求非负整数,所以最后要模成正的
code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define N 21
#define mod 998244353
#define S (1<<20)
ll n,all;
ll a[N];
ll sum[S];
ll f[S],g[S];
ll ans=0;
int main()
{
scanf("%lld",&n);
all=(1<<n)-1;
for(int i=0;i<n;i++)scanf("%lld",&sum[1<<i]);
for(int i=0;i<=all;i++)sum[i]=(sum[i&(-i)]+sum[i^(i&-i)])%mod;
g[0]=1;
for(int i=0;i<=all;i++)
{
if(sum[i]<0)
{
for(int j=0;j<n;j++)
{
if(i&(1<<j))
{
g[i]=(g[i]+g[i^(1<<j)])%mod;
}
}
}
}
f[0]=1;
for(int i=0;i<=all;i++)
{
if(sum[i]>=0)
{
for(int j=0;j<n;j++)
{
if((i&(1<<j))==0)
{
f[i|(1<<j)]=(f[i|(1<<j)]+f[i])%mod;
}
}
}
}
for(int i=0;i<=all;i++)
{
//cout<<sum[i]<<' '<<f[i]<<' '<<g[all^i]<<endl;
ans=(ans+sum[i]*f[i]%mod*g[all^i]%mod+mod)%mod;
}
printf("%lld",ans);
return 0;
}
「刷题笔记」DP优化-状压-EX的更多相关文章
- 【刷题笔记】DP优化-状压
因为篇幅太长翻着麻烦,计划把DP拆成几个小专题,这里原文只留下状压,其他请至后续博文. 状态压缩优化 所谓状态压缩,就是将原本需要很多很多维来描述,甚至暴力根本描述不清的状态压缩成一维来描述. 时间复 ...
- 「刷题笔记」AC自动机
自动AC机 Keywords Research 板子题,同luoguP3808,不过是多测. 然后多测不清空,\(MLE\)两行泪. 板子放一下 #include<bits/stdc++.h&g ...
- 「刷题笔记」哈希,kmp,trie
Bovine Genomics 暴力 str hash+dp 设\(dp[i][j]\)为前\(i\)组匹配到第\(j\)位的方案数,则转移方程 \[dp[i][j+l]+=dp[i-1][j] \] ...
- 【刷题笔记】DP优化-斜率优化
斜率优化,是一种利用斜率的优化(废话) 关于数论:咕咕咕 部分内容参考自学长 如果有这样的一个状态转移方程: \[f[i]=\min\limits_{j=L_j}^{R_j}\{f[j]+val(j, ...
- 【刷题笔记】DP优化-单调队列优化
单调队列优化 眼界极窄的ZZ之前甚至不会单调队列--(好丢人啊) 单调队列优化的常见情景: 转移可以转化成只需要确定一个维度,而且这个维度的取值范围在某个区间里 修剪草坪 这个题学长讲的好像是另外一个 ...
- 「刷题笔记」LCA问题相关
板子 ll lg[40]; ll dep[N],fa[N][40]; ll dis[N]; void dfs(ll u,ll f) { dep[u]=dep[f]+1; fa[u][0]=f; for ...
- 「刷题笔记」Tarjan
贴一个讲得非常详细的\(tarjan\)入门教程 信息传递 讲个笑话:我之前用并查集求最小环过的这题,然后看见题目上有个\(tarjan\)标签 留下了深刻的印象:\(tarjan\)就是并查集求最小 ...
- 《Data Structures and Algorithm Analysis in C》学习与刷题笔记
<Data Structures and Algorithm Analysis in C>学习与刷题笔记 为什么要学习DSAAC? 某个月黑风高的夜晚,下班的我走在黯淡无光.冷清无人的冲之 ...
- PTA刷题笔记
PTA刷题记录 仓库地址: https://github.com/Haorical/Code/tree/master/PTA/GPLT 两周之内刷完GPLT L2和L3的题,持续更新,包括AK代码,坑 ...
随机推荐
- Android操作系统及APP
1. Android操作系统 1.1. 介绍 Android操作系统最初由Andy Rubin开发,主要支持手机.2005年8月由Google收购注资.第一部Android智能手机发布于2008 ...
- 【Kata Daily 190929】Password Hashes(密码哈希)
题目: When you sign up for an account somewhere, some websites do not actually store your password in ...
- 【Kata Daily 190910】Who likes it?(谁点了赞?)
题目: Description: You probably know the "like" system from Facebook and other pages. People ...
- python3批量修改文件后缀名
import os # 原文件后缀名 suffix_name = '.jar.src.zip' # 新文件后缀名 nwe_suffix_name = '.jar' def foo(path1): fi ...
- layui table表格详解
上次做table有些东西 忘记了 这次当作来个分析总结一下 跟大家共同学习 闲话不多说 直接上例子 代码: <form id="form1" runat="s ...
- MSSQL 指定分隔符号 生成数据集
DECLARE @xml VARCHAR(MAX)='磨毛:1 缩率:2 干磨:3 湿摩:4 水洗牢度:5 手感:6 防水:7 PH:8 日晒:9' SET @xml= '<root>'+ ...
- SSM工作流程与原理详解
自学ssm->springboot->springcloud,所以很多东西会用但理解较浅,所以现在从最开始的ssm开始进行对原理以及运行过程的整理知识归纳,若有错误感谢指正. Spring ...
- vs code远程开发
VS Code如何配置远程开发 你是如何远程开发的?还在使用FTP/SFTP同步文件?那你out了,有了宇宙第一IDE:VS就不需要这么麻烦了,一起学习一下吧. 第一步,安装Remote SSH插件 ...
- tcpack----- 2sack dack
道当我们接收到ack的时候,我们会判断sack段,如果包含sack段的话,我们就要进行处理.这篇blog就主要来介绍内核如何处理sack段 sack dsack概念 TCP在一个RTO超时后会重传数据 ...
- cephonebox发布
前言 现在已经是2016年收官的一个月了,之前一直想做一个calamari的集成版本,之所以有这个想法,是因为,即使在已经打好包的情况下,因为各种软件版本的原因,造成很多人无法配置成功,calamar ...