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;
}

ZR979B. 【十联测 Day 9】唯一睿酱的更多相关文章

  1. 「NOI十联测」深邃

    「NOI十联测」深邃 要使得最大的连通块最小,显然先二分答案. 先固定1结点为根. 对于一个果实,显然是先处理子树中未分配的点,再向外延伸. 每个结点记录一个\(si[]\),表示子树中未分配的点数, ...

  2. 「NOI十联测」奥义商店

    「NOI十联测」奥义商店 若lzz想花费最少的钱,那么显然要选择数目较少的颜色. 先考虑暴力的写法. 每次向两边统计,每个物品要求被买的概率可以由上一个物品推出. now=1;//now 被买概率 M ...

  3. 「NOI十联测」黑暗

    「NOI十联测」黑暗 \(n\) 个点的无向图,每条边都可能存在,一个图的权值是连通块个数的 \(m\) 次方,求所有可能的图的权值和.(n≤30000,m≤15) 令\(ans[n][m]\)为n个 ...

  4. 「NOI十联测」反函数

    30pts 令(为1,)为-1: 暴力枚举每个点为起始点的路径,一条路径是合法的当且仅当路径权值和为0且路径上没有出现过负数. 将所有答案算出. 100pts 使用点分治. 要求知道经过重心root的 ...

  5. 常见分布式唯一ID生成策略

    方法一: 用数据库的 auto_increment 来生成 优点: 此方法使用数据库原有的功能,所以相对简单 能够保证唯一性 能够保证递增性 id 之间的步长是固定且可自定义的 缺点: 可用性难以保证 ...

  6. 支持向量机(SVM)

    断断续续看了好多天,赶紧补上坑. 感谢july的 http://blog.csdn.net/v_july_v/article/details/7624837/ 以及CSDN上淘的比较正规的SMO C+ ...

  7. js 常用代码片段

    一.预加载图像 如果你的网页中需要使用大量初始不可见的(例如,悬停的)图像,那么可以预加载这些图像. function preloadImages(){ for(var i=0;i<argume ...

  8. YouTube Cobalt 浏览器支持

    Cobalt介绍: Cobalt浏览器是YouTube公司定制的一款专用浏览器,Cobalt的使命,是在电视机端,使用灵活多变web形式实现流畅的交互操作,从而替代Android,与普通浏览器不同,C ...

  9. 国内NLP的那些人那些会

    统计学和语言学专家都列在一起了,没有区分.1,黄昌宁,1937年生于广东,1955年考入清华大学电机系,1961年毕业并留校任教至博士生导师, 1983-1984年赴美国耶鲁大学进修,1986-198 ...

随机推荐

  1. JavaScript--for in循环访问属性用"."和[ ]的区别

    // for in 循环遍历对象的时候// 内部要访问属性的时候不能点语法访问,因为for in 的key是字符串格式// 可通过方括号实现访问 for(var key in manObj) { co ...

  2. Best Time to Sell and Buy Stock

    这道题想了很多,但是想多了.这个题思路很简单,如果当前值大于最小值,就计算差,和最大利润值比较. class Solution { public: int maxProfit(vector<in ...

  3. Spring MVC 关于controller的字符编码问题

    在使用springMVC框架构建web应用,客户端常会请求字符串.整型.json等格式的数据,通常使用@ResponseBody注解使 controller回应相应的数据而不是去渲染某个页面.如果请求 ...

  4. 2019-8-31-C#-通过-probing-指定-dll-寻找文件夹

    title author date CreateTime categories C# 通过 probing 指定 dll 寻找文件夹 lindexi 2019-08-31 16:55:58 +0800 ...

  5. 阿里开源新一代 AI 算法模型,由达摩院90后科学家研发

    最炫的技术新知.最热门的大咖公开课.最有趣的开发者活动.最实用的工具干货,就在<开发者必读>! 每日集成开发者社区精品内容,你身边的技术资讯管家. 每日头条 阿里开源新一代 AI 算法模型 ...

  6. 2016年中国的SaaS服务商企业研究

    近年来,随着中国人口红利的消退及移动互联网红利的凸显,让中国的To C创业,尤其是O2O领域的创业经历了一波高潮.2015年末,一场"资本寒冬"让O2O创业趋于理性,但SaaS及T ...

  7. mysql列转行 行转列

    列转行 SELECT flag ,substring_index(substring_index(t.context,), ) as result FROM ( select 'aa' as flag ...

  8. Jmeter xpath处理器

  9. oracle选择最有效率的表名顺序

    ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表 driving table)将被最先处理. 在FROM子句中包含多个表的情况下,你必须选择记录条 ...

  10. oracle函数 NLS_LOWER(x[,y])

    [功能]返回字符串并将字符串的变为小写; [参数]x字符型表达式 [参数]Nls_param可选,指定排序的方式(nls_sort=) . SCHINESE_RADICAL_M(部首.笔画) SCHI ...