棋盘

需要注意的几点:

  • 题面编号都是从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. 基于SLF4J的MDC机制和Dubbo的Filter机制,实现分布式系统的日志全链路追踪

    原文链接:基于SLF4J的MDC机制和Dubbo的Filter机制,实现分布式系统的日志全链路追踪 一.日志系统 1.日志框架 在每个系统应用中,我们都会使用日志系统,主要是为了记录必要的信息和方便排 ...

  2. MIPS学习笔记(一)

    写在前面 本文是根据"MIPS Assembly Language Programming CS50 Discussion and Project Book. Daniel J. Ellar ...

  3. 840. Magic Squares In Grid ——weekly contest 86

    题目链接:https://leetcode.com/problems/magic-squares-in-grid/description attention:注意给定的数字不一定是1-9. time: ...

  4. 面试 之 nginx,负载,动静分离

    大家先看这个逻辑图 为什么我们要这样去架构我们的一个项目呢? 这样做的话,动态请求要先访问 A,A 转发访问 B,再由 B 返回结果给 A,A 最后又将结果返回给客户端这样是不是很麻烦? 最初开发的时 ...

  5. js练习题之查找数组中的位子

    输出描述: 如果数组中存在 item,则返回元素在数组中的位置,否则返回 -1 输入例子: indexOf([ 1, 2, 3, 4 ], 3) 输出例子: 2 function indexOf(ar ...

  6. ps怎么做发光字体效果 ps中最简单的发光字教程

    ps中最简单的发光字教程 我们先用[文字工具]输入文字(比如:发光效果),字体填充为白色,如图所示. 我们选中文字的图层,点击[FX]找到[外发光],如图所示. 接着,我们在外发光里面把颜色设置为紫色 ...

  7. Python练手项目实例汇总(附源码下载)

    今天给大家分享几个有趣的Python练手项目实例,希望对Python初学者有帮助哈~ 一.经典的俄罗斯方块   1. 绑定功能 1 # 绑定功能 2 class App(Frame): 3 def _ ...

  8. ORA-00020: maximum number of processes (40) exceeded模拟会话连接数满

    问题描述:在正式生产环境中,有的库建的process和session连接数目设置的较小,导致后期满了无法连接.因为正式库无法进行停库修改,只能释放连接,做个测试模拟 1. 修改现有最大会话与进程连接数 ...

  9. ERP中HR模块的操作与设计--开源软件诞生26

    赤龙ERP的EHR功能讲解--第26篇 用日志记录"开源软件"的诞生 [进入地址 点亮星星]----祈盼着一个鼓励 博主开源地址: 码云:https://gitee.com/red ...

  10. [转自王垠]完全用GNU/Linux工作,摈弃Windows低效率的工作方式

    ZT (a qinghua student's article) 我已经半年没有使用 Windows 的方式工作了.Linux 高效的完成了我所有的工作. GNU/Linux 不是每个人都想用的.如果 ...