「刷题笔记」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代码,坑 ...
随机推荐
- nginx反向代理三台web服务器,实现负载均衡
修改nginx.conf #在http和server之间加入这个模块 upstream guaji{ server 127.0.0.1:8080; server 127.0.0.2:8080; ser ...
- python爬虫scrapy框架
Scrapy 框架 关注公众号"轻松学编程"了解更多. 一.简介 Scrapy是用纯Python实现一个为了爬取网站数据.提取结构性数据而编写的应用框架,用途非常广泛. 框架的力量 ...
- python数据类型之String(字符串)
String(字符串) 1.概述 字符串是以单引号或双引号括起来的任意文本,比如"abc",'xy'等等,请注意''或者""本身只是一种表示方式,并不是字符 ...
- 如何在Windows Server 2012及更高版本中将域控制器降级
如何在Windows Server 2012及更高版本中将域控制器降级 如果不降级就重装系统,会出问题,所以在将域控系统重装系统之前一定要先降级. 使用服务器管理器将 Windows Server 2 ...
- Git的全局及单个仓库配置
我们先来了解一下在git中的配置文件路径: /etc/gitconfig 文件: 包含系统上每一个用户及他们仓库的通用配置. 如果在执行 git config 时带上 --system 选项,那么它就 ...
- vim实现CTRL+S为保存快捷键
用vim正撸代码撸的飞起,突然Xshell就掉线了,真是太蛋疼了. 于是开始怀念起vs下撸代码时随时随地ctrl+s保存的快捷键,百度了一下,网上的vim实现ctrl+s保存的快捷键设置都有问题,自己 ...
- Servlet程序访问jsp文件404的一种情况
启动Jsp Run on Server的时候出现404的错误,如下图: 检查一下是否文档目录如下图:jsp应该在WebContent下,而不是WEB_INF下,访问放在WEB_INF下的jsp文件就会 ...
- 查看并配置JAVA_HOME
转载自https://blog.csdn.net/weixin_41713592/article/details/79941418which java [root@host2 hadoop-1.1.2 ...
- IDEA与Eclipse创建struts项目
1.IDEA创建struts项目 这里再构建struts项目是选择jar包出问题了,可以重新配置 创建页面和action配置struts.xml 启动tomcat,浏览器中运行 具体参考: https ...
- TCP中RTT的测量和RTO的计算
https://blog.csdn.net/zhangskd/article/details/7196707 tcp传输往返时间是指:发送方发送tcp断开时, 到发送方接收到改段立即响应的所耗费的时间 ...