2019CCPC Harbin I - Interesting Permutation
题目大意:
现有一个序列a,定义
\(f_i=MAX(a_1,a_2,...,a_i)\)
\(g_i=MIN(a_1,a_2,..,a_i)\)
\(h_i=f_i-g_i\)
$ 1\leq n\leq1e5$
解题思路:
首先判断一下h数组的合法性,若已判定h数组合法:
对于 \(h_i>h_{i-1},\)那么第\(i\)个位置只能作为当前的最大值或者最小值,答案乘以2,
否则,第\(i\)个位置可以选择放置最大值和最小值之间的可选择的数字,可以理解成插空,假设有cnt个可选择的数字,答案乘以cnt,放置之后cnt-=1;
同时对于 \(h_i>h_{i-1}\) 来说,会新产生 \(h_i-h_{i-1}-1\)的空位,将至加到cnt中即可。
代码
#include<bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N=1e6+5;
const LL mod=1e9+7;
#define fi first
#define se second
#define ls (i<<1)
#define rs (i<<1|1)
#define mem(a,b) memset(a,b,sizeof(a))
#define eb emplace_back
#define mk make_pair
LL read()
{
LL x=0,t=1;
char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-') t=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<3)+(x<<1)+ch-'0'; ch=getchar(); }
return x*t;
}
int h[N];
int main()
{
int T=read();
while(T--)
{
int n=read();
for(int i=1;i<=n;i++) h[i]=read();
LL ans=1;
for(int i=1;i<=n;i++)
{
if(h[i]<h[i-1]||h[i]+1<i||h[i]>=n) ans=0;
}
if(!ans) {
printf("0\n");
continue;
}
int cnt=0;
for(int i=2;i<=n;i++)
{
if(h[i]>h[i-1]) ans=2*ans%mod,cnt+=h[i]-h[i-1]-1;
else ans=ans*(cnt--)%mod;
}
printf("%lld\n",ans);
}
return 0;
}
对于排列计数的问题,可以考虑从第\(i\)个位置的关于\(1-i\) 的排列方案 转移到 第\(i+1\)个位置关于\(1-i+1\)的排列方案,比如这道2019ccpc哈尔滨的I题,假定初始\(a_1=1\),如果\(i->i+1,h_{i+1}>h_i\) ,那么当前第\(i+1\)个数就只能是最大或最小值,放置最大值就直接基于之前\(1-i\) 放置一个 \(Max+d\), \(d\)为\(h_{i+1}-h_i\),或者放置最小值\(Min-d\),,然后将排列整体向右或向左平移,平移是一对一的映射,平移后方案数不变(如果一个排列放置\(Max+d\)或\(Min-d\)越界,那么他的对称在放置\(Min-d\)或\(Max+d\)时也会越界),因此不会影响计数。