棋盘

需要注意的几点:

  • 题面编号都是从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\)的期望和

如果新取一个负数答案应该是直接加,取正数就得更新,但是好像也不一定会更新……

是不是得记录下当前值和更新答案的临界值什么的……

或者用状压记录负数位置……?

(实际证明没一点挨上边)

正解

妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊妙啊

状压还能这么出的吗,爱了爱了(虽然做不出来吧……

深挖一下最大前缀和的性质:

\[\max(\sum\limits_{j=1}^{i} a_j),i\in [1,n]
\]

设数列\(A\)的最大前缀和为\(pre_j\),即\(\sum\limits_{i=1}^{j} a_i\)

那么由此可以推出:

  1. 对于任意\(1\leq x \leq j\),有\(\sum\limits_{i=x}^{j} A_i\)定不小于0(否则可以把这部分减去)
  2. 对于任意\(j+1\leq x \leq n\),有\(\sum\limits_{i=j+1}^{x} A_i\)定不大于0(否则可以把这部分加上)

然后,我们发现,原数列可以分成两部分,分别满足不同的条件,既然如此,我们把满足左边条件的排列个数与满足右边条件的排列个数相乘,就可以得到总方案个数。

但是,题目中要求我们求最大前缀和的期望\(\cdot n!\),也就是用每种可能值乘上能得到它的方案个数,所以,结合状压,我们可以将每种状态满足左右的排列个数相乘,再把每种状态的满足条件方案个数乘上每种状态选的数的总和,即为所求

对于右边,我们在一个所有前缀和为负的数列后面加一个数,若新数列总和仍然为负,则新数列所有前缀和为负

对于左边,我们在一个所有后缀和为正的数列前面加一个数,若新数列总和仍然为非负,则新数列所有后缀和为非负

基于这样的过程,我们可以枚举子集,则有

\[g[i]=\sum g[i-j](j\in i,sum[i]< 0),f[i+j]+=f[i](j\notin i,sum[i]\geq 0)
\]

答案为

\[\sum\limits_{i=0}^{all} sum[i]*f[i]*g[all\ xor\ i]
\]

另外注意:答案要求非负整数,所以最后要模成正的

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的更多相关文章

  1. 【刷题笔记】DP优化-状压

    因为篇幅太长翻着麻烦,计划把DP拆成几个小专题,这里原文只留下状压,其他请至后续博文. 状态压缩优化 所谓状态压缩,就是将原本需要很多很多维来描述,甚至暴力根本描述不清的状态压缩成一维来描述. 时间复 ...

  2. 「刷题笔记」AC自动机

    自动AC机 Keywords Research 板子题,同luoguP3808,不过是多测. 然后多测不清空,\(MLE\)两行泪. 板子放一下 #include<bits/stdc++.h&g ...

  3. 「刷题笔记」哈希,kmp,trie

    Bovine Genomics 暴力 str hash+dp 设\(dp[i][j]\)为前\(i\)组匹配到第\(j\)位的方案数,则转移方程 \[dp[i][j+l]+=dp[i-1][j] \] ...

  4. 【刷题笔记】DP优化-斜率优化

    斜率优化,是一种利用斜率的优化(废话) 关于数论:咕咕咕 部分内容参考自学长 如果有这样的一个状态转移方程: \[f[i]=\min\limits_{j=L_j}^{R_j}\{f[j]+val(j, ...

  5. 【刷题笔记】DP优化-单调队列优化

    单调队列优化 眼界极窄的ZZ之前甚至不会单调队列--(好丢人啊) 单调队列优化的常见情景: 转移可以转化成只需要确定一个维度,而且这个维度的取值范围在某个区间里 修剪草坪 这个题学长讲的好像是另外一个 ...

  6. 「刷题笔记」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 ...

  7. 「刷题笔记」Tarjan

    贴一个讲得非常详细的\(tarjan\)入门教程 信息传递 讲个笑话:我之前用并查集求最小环过的这题,然后看见题目上有个\(tarjan\)标签 留下了深刻的印象:\(tarjan\)就是并查集求最小 ...

  8. 《Data Structures and Algorithm Analysis in C》学习与刷题笔记

    <Data Structures and Algorithm Analysis in C>学习与刷题笔记 为什么要学习DSAAC? 某个月黑风高的夜晚,下班的我走在黯淡无光.冷清无人的冲之 ...

  9. PTA刷题笔记

    PTA刷题记录 仓库地址: https://github.com/Haorical/Code/tree/master/PTA/GPLT 两周之内刷完GPLT L2和L3的题,持续更新,包括AK代码,坑 ...

随机推荐

  1. css 背景图片自适应分辨率大小 兼容

    拉伸,all浏览器兼容.bg{     background:url(http://wyz.67ge.com/wp-content/uploads/qzlogo.jpg);     filter:&q ...

  2. nginx vhost配置

    server { listen 80; server_name crsdemo.my; index index.html index.htm index.php default.html defaul ...

  3. layui导航

    关于导航 首先看一下官网的样式: <!DOCTYPE html><html><head> <meta charset="utf-8" /& ...

  4. Hopfield Network 霍普菲尔德网络入门

    简介 Hopfield Network (霍普菲尔德网络),是 Hopfield 在1982年提出的一种基于能量的模型,发表的文章是 Neural networks and physical syst ...

  5. MySQL全面瓦解9:查询的排序、分页相关

    概述 数据库中的数据直接呈现出来一般不是我们想要的,所以我们上两节演示了如何对数据进行过滤的方法.除了对数据进行过滤, 我们可能还需要对数据进行排序,比如想从列表中了解消费最高的项,就可能需要对金额字 ...

  6. UNIX目录访问操作

    1.目录访问相关函数: DIR* opendir (const char * path ); struct dirent* readdir(DIR *dirptr) ;参数是一个指向dirent 结构 ...

  7. SpringBoot第十一集:整合Swagger3.0与RESTful接口整合返回值(2020最新最易懂)

    SpringBoot第十一集:整合Swagger3.0与RESTful接口整合返回值(2020最新最易懂) 一,整合Swagger3.0 随着Spring Boot.Spring Cloud等微服务的 ...

  8. MFC的消息响应机制说明

    MFC的快速理解: 1.MFC的设计者们在设计MFC时,有一个主要的方向就是尽可能使得MFC的代码要小,速度尽可能快.为了这个方向,工程师们使用了许多技巧,主要表现在宏的运用上,实 现MFC的消息映射 ...

  9. appium-appium的等待时间

    #三种appium设置等待时间的方法 #第一种 sleep(): 设置固定休眠时间. python 的 time 包提供了休眠方法 sleep() , 导入 time包后就可以使用 sleep()进行 ...

  10. 阿里巴巴已offer:Java实习五面详细面经(附解答)

    1.岗位 Java后台开发实习生 2.时间表 2020/3/18 提交简历 & 测评 2020/3/23 笔试 2020/3/26 简历面 2020/4/11 技术一面 2020/4/14 技 ...