ZR979B. 【十联测 Day 9】唯一睿酱

题目大意:

给定一个数组\(r_i\),表明对于第\(i\)个数来说,他是\([max(1,i - r_i),min(n,i+r_i)]\)中最大的,求有多少\(1-n\)的排列的\(r _i\)与给定的相同

\(n \le 5000\)

部分分及解题步骤

\(n \le 10\)

枚举全排列即可

\(n \le 17\)

首先这个数据范围要考虑状压DP,有关排列计数的题目,一般又要利用题目中给出的特殊性质,所以并没有所谓的套路化思维可言

所以,因为这道题和排列的大小关系有关,所以我们考虑状压DP的时候从小往大地向里面加数,设\(f_S\)表示现在\(S\)这些位置的数已经被填上了,同时满足条件的方案数

转移就枚举最后一次填了哪一个数

由于我们是从小往大去填,对于对于所有的状态为\(1\)的地方,都是比这个数要小的,同理\(0\)就是要大的,我们就可以轻松的计算出这个位置填最大值是否合法,进而转移

时间复杂度:\(O(2^nn^2)\)

\(n\le 500\)

注意上一段中我加深的部分,我们状压DP的本质是判断最大值在这个位置是否合法,这也在启示我们多项式时间复杂度的做法:

枚举最大值,类似区间DP的合并

设\(f_{l,r}\)表示\([l,r]\)这些位置的满足条件的合法排列个数,注意这个地方的所谓的排列个数实际上是\(1\)到\((r - l + 1)\)的一个排列,因为只关心大小关系的时候,排列的子集也可以看做一个排列,在合并的时候通过组合计数合成一个大的排列即可,那么我们就枚举最大值的位置\(k\)来进行转移,那么首先这个\(k\)应该满足$k - r_k = l \(或者\)k + r_k = r\(,否则就不可能成为最大值,其次还要满足\)\forall i \in [l,k - 1],i+r_i < k\(和\)\forall i\in [k + 1,r],i -r_i>k$如果左右两个区间存在一个比他更大的数,也显然不能转移

接下来想如何合并两个区间

你刚才说过,排列的子集也是一个排列,所以我们就可以得到的方程

\[f_{l,r} = \sum_{k} f_{l,k - 1} \times f_{k + 1,r} \times \binom{r - l}{k - l}
\]

首先,\(k\)应该满足上述条件,\(\binom{r - l}{k - l}\)的意思是一共有\(r - l\)个数(因为最大值的位置是固定的),选择\(k - l\)个放在左边,注意边界处理即可

\(n \le 5000\)

首先,我们可以发现,一个\(k\)最多也只可能会对\(k - r_k\)和\(k+r_k\)产生贡献,所以我们直接

设\(G_l\)表示能对\(l\)产生贡献的点,直接转移即可省掉枚举最大值的位置优化到\(O(n ^2)\)

总结

枚举最大值最小值可能是解题关键

首先排列的子集看做排列这一点在只关系大小关系的技术问题上十分耐用

在区间DP的时候,有时候\(f_{i,i - 1}\)的值要仔细斟酌

代码

40opts:状压DP

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#include<set>
#include<map>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = (1 << 17) + 50;
const int mod = 1e9 + 7;
int n,m;
int f[N];
int r[N];
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline int count(int x){
int res = 0;
while(x){
if(x & 1) res++;
x >>= 1;
}
return res;
}
inline int get(int x,int pos){
int maxx = 0x3f3f3f3f;
int now = pos - 1;
while(now >= 0 && (x & (1 << now))) now--;
maxx = min(maxx,pos - now - 1);
now = pos + 1;
while(now < n && (x & (1 << now))) now++;
return min(maxx,now - pos - 1);
}
inline int mo(int x){
if(x >= mod) x -= mod;
return x;
}
int main(){
n = read();
for(int i = 1;i <= n;++i) r[i] = read();
f[0] = 1;
for(int i = 1;i < (1 << n);++i){
for(int j = 0;j < n;++j){
if(!(i & (1 << j))) continue;
if(get(i,j) == r[j + 1]) f[i] = mo(f[i] + f[i ^ (1 << j)]);
}
}
printf("%d\n",f[(1 << n) - 1]);
return 0;
}

80opt:n^3区间DP

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#include<set>
#include<map>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 5005;
const LL mod = 1e9 + 7;
LL f[N][N];
LL C[N][N];
int n;
int mr[N][N],ml[N][N];
int a[N];
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline int mo(LL x){
if(x >= mod) x -= mod;
return x;
}
inline LL dfs(int l,int r){
if(l >= r) return 1;
if(f[l][r] != -1) return f[l][r];
f[l][r] = 0;
for(int k = l;k <= r;++k){
if((k - a[k] == l || k + a[k] == r) && mr[l][k - 1] < k && ml[k + 1][r] > k){
f[l][r] = mo(f[l][r] + dfs(l,k - 1) * dfs(k + 1,r) % mod * C[r - l][k - l] % mod);
}
}
return f[l][r];
}
int main(){
memset(f,-1,sizeof(f));
n = read();
for(int i = 1;i <= n;++i) a[i] = read();
for(int i = 0;i <= n;++i){
C[i][0] = 1;
for(int j = 1;j <= i;++j) C[i][j] = mo(C[i - 1][j] + C[i - 1][j - 1]);
}
for(int i = 1;i <= n;++i){
ml[i][i - 1] = n + 1;
mr[i][i - 1] = 0;
for(int j = i;j <= n;++j){
ml[i][j] = min(j - a[j],ml[i][j - 1]);
mr[i][j] = max(j + a[j],mr[i][j - 1]);
}
}
ml[n + 1][n] = n + 1;
printf("%lld\n",dfs(1,n));
return 0;
}

100opt n^2区间DP

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<vector>
#include<ctime>
#include<cmath>
#include<set>
#include<map>
#define LL long long
#define pii pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
const int N = 5005;
const LL mod = 1e9 + 7;
LL f[N][N];
int C[N][N];
int n;
int mr[N][N],ml[N][N];
int a[N];
vector <int> Gl[N << 1],Gr[N << 1];
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
inline int mo(LL x){
if(x >= mod) x -= mod;
return x;
}
inline LL dfs(int l,int r){
if(l >= r) return 1;
if(f[l][r] != -1) return f[l][r];
f[l][r] = 0;
int to = lower_bound(Gl[l].begin(),Gl[l].end(),l) - Gl[l].begin();
for(int k = to;k < (int)Gl[l].size();++k){
if(Gl[l][k] > r) break;
int i = Gl[l][k];
if(mr[l][i - 1] < i && ml[i + 1][r] > i) f[l][r] = mo(f[l][r] + dfs(l,i - 1) * dfs(i + 1,r) % mod * C[r - l][i - l] % mod);
}
to = lower_bound(Gr[r].begin(),Gr[r].end(),l) - Gr[r].begin();
for(int k = to;k < (int)Gr[r].size();++k){
if(Gr[r][k] > r) break;
int i = Gr[r][k];if(i - a[i] == l) continue;
if(mr[l][i - 1] < i && ml[i + 1][r] > i) f[l][r] = mo(f[l][r] + dfs(l,i - 1) * dfs(i + 1,r) % mod * C[r - l][i - l] % mod);
}
return f[l][r];
}
int main(){
memset(f,-1,sizeof(f));
n = read();
for(int i = 1;i <= n;++i) a[i] = read();
for(int i = 0;i <= n;++i){
C[i][0] = 1;
for(int j = 1;j <= i;++j) C[i][j] = mo(C[i - 1][j] + C[i - 1][j - 1]);
}
for(int i = 1;i <= n;++i){
ml[i][i - 1] = n + 1;
mr[i][i - 1] = 0;
for(int j = i;j <= n;++j){
ml[i][j] = min(j - a[j],ml[i][j - 1]);
mr[i][j] = max(j + a[j],mr[i][j - 1]);
}
}
ml[n + 1][n] = n + 1;
for(int i = 1;i <= n;++i){
Gl[i - a[i]].push_back(i);
Gr[i + a[i]].push_back(i);
}
for(int i = 1;i <= 2 * n;++i){
sort(Gl[i].begin(),Gl[i].end());
sort(Gr[i].begin(),Gr[i].end());
}
printf("%lld\n",dfs(1,n));
return 0;
}

最新文章

  1. 老王讲自制RPC框架.(一.前言与技术选型)
  2. 给vs2010换皮肤
  3. 黑马程序员——【Java高新技术】——类加载器
  4. Linux中的SWAP交换分区
  5. 【转】最长回文子串的O(n)的Manacher算法
  6. matlab 绘制条形图
  7. [attribute] 匹配包含给定属性的元素
  8. asp.net 间传值的方法
  9. 实现3D摄像机缓冲系统的一些思考
  10. struct ifreq结构体与ip,子网掩码,网关等信息
  11. Javascript-one
  12. Cocos2d-x 3.x plist+png 做动画
  13. scrapy学习笔记
  14. Entity Framework查询注意
  15. [ExtJS5学习笔记]第三十六节 报表组件mzPivotGrid
  16. Python第二天 变量 运算符与表达式 input()与raw_input()区别 字符编码 python转义符 字符串格式化 format函数字符串格式化 帮助
  17. 排序算法系列:快速排序算法JAVA版(靠谱、清晰、真实、可用、不罗嗦版)
  18. AI 循环神经网络
  19. 中间件安全加固之Jboss
  20. android适配各种分辨率的问题

热门文章

  1. 获取文章,显示时自动换行(word-break与 work-wrap的区别)
  2. MUI - H5实现ios长按图标后进入图标排序及删除功能的效果
  3. celery 动态定时任务探索
  4. postman 百度网盘下载 64位
  5. oracle copy
  6. TIJ——Chapter Nine:Interfaces
  7. 13条必知必会&amp;&amp;测试
  8. UVa 10285【搜索】
  9. Android实现圆角边框
  10. 错误处理——According to TLD or attribute directive in tag file, attribute test does not accept any expres