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. 【JZOJ4788】【NOIP2016提高A组模拟9.17】序列

    题目描述 输入 输出 样例输入 1 5 2 1 3 0 3 2 2 0 1 0 样例输出 1 数据范围 解法 考虑没有模的情况,问题就仅仅只是简单的差分问题(广告铺设): 设r[i]是第i位需要加的次 ...

  2. 【JZOJ4787】【NOIP2016提高A组模拟9.17】数格子

    题目描述 输入 输出 样例输入 1 10000 3 10000 5 10000 0 0 样例输出 1 11 95 数据范围 每个测试点数据组数不超过10组 解法 状态压缩动态规划. 设f[i][j]表 ...

  3. Oracle函数——MINUS

    解释 “minus”直接翻译为中文是“减”的意思,在Oracle中也是用来做减法操作的,只不过它不是传统意义上对数字的减法,而是对查询结果集的减法.A minus B就意味着将结果集A去除结果集B中所 ...

  4. Selenium-----wait的三种等待

    在UI自动化测试中,必然会遇到环境不稳定,网络慢的情况,这时如果你不做任何处理的话,代码会由于没有找到元素,而报错.这时我们就要用到wait(等待),而在Selenium中,我们可以用到一共三种等待, ...

  5. MVC设计之MVC设计模式(介绍)

    mvc介绍; 首先先引用一个百度百科的介绍: MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用 ...

  6. hdu3472 混合图判断欧拉通路

    对于欧拉回路,先判断出度入度的差是否为偶数,然后最大流一次. 此题是判断有无欧拉通路,前提要判断图是否连通,然后欧拉通路的条件:要么出入度差没有奇数,或者只有2个点. 所以先统计差为奇数的个数,如果不 ...

  7. 【转】Sprague-Grundy函数

    http://www.cnitblog.com/weiweibbs/articles/42735.html 上一期的文章里我们仔细研究了Nim游戏,并且了解了找出必胜策略的方法.但如果把Nim的规则略 ...

  8. MacOS代理设置(桌面应用代理设置&Terminal代理设置)

    MacOS代理分为桌面应用代理设置&Terminal代理设置,使用代理软件默认只会开启桌面应用代理,Terminal代理需要单独配置   桌面应用代理设置 Terminal查看桌面应用代理设置 ...

  9. C++类继承中的虚方法

    #include <bits/stdc++.h> using namespace std; class A { public: void Show() { cout << &q ...

  10. 一维数组的初始化及遍历 Day06

    package com.sxt.arraytest1; import java.util.Arrays; /* * 一维数组 */ public class ArrayTest2 { public s ...