Description
Bobo 在 ICPCCamp 学会了解决最长上升子序列问题后得到了一个长度为 n 的数列 p
1,p
2,…,p
n.
Bobo 想用 1,2,…,n 来替换其中值为 0 的元素,使得 p
1,p
2,…,p
n 互不相同(即 p
1,p
2,…,p
n 是 {1,2,…,n} 的排列)。
现在 Bobo 想知道,替换后最长上升子序列的长度恰好为 (n-1) 数列的数量。
Input
输入包含不超过 300 组数据,其中不超过 20 组的 n 超过 100.
每组数据的第一行包含一个整数 n (1≤n≤10
5).
第二行包含 n 个整数p
1,p
2,…,p
n (0≤p
i≤n).
保证p
1,p
2,…,p
n中非 0 的元素互不相同。
Output
对于每组数据,输出一个整数表示要求的值。
Sample Input
3
0 0 0
4
0 0 0 0
5
1 0 0 4 5
Sample Output
4 91
先递推出dp[i]表示长度为i且最长上升子序列的长度恰好为 (n-1) 的序列的个数。
考虑第i个数放的位置,如果放在第i位,那么有dp[i-1]种,如果放在i-1位,有i-1种
放在前面i-2个位置分别有一种,所以dp[i]=dp[i-1]+2i-3;
对于一个长度为n的合法序列来说,必然是从有序的1,2,3,4,5..n
这个数列中向前或向后移动某一个数字得到的
所以对于给出的序列,分类讨论
1.如果有某个数字偏离值超过1,那么显然此时答案是只有1种或没有,判断一下即可。
2.如果有某两个数字偏离分别是+1和-1,那么同上,判断一下即可。
3.如果有偏离,判断是-1的还是+1的,对于这种情况,
找出最左和最右的偏离,并且中间有的数也必须是偏离的,
然后答案是左边可用的空间和右边可用的空间的乘积。
4.没有任何位置有偏移,那么找出全部连续的可用空间,把对应的dp值加起来即可。
#include<set> #include<map> #include<ctime> #include<cmath> #include<stack> #include<queue> #include<bitset> #include<cstdio> #include<string> #include<cstring> #include<iostream> #include<algorithm> #include<functional> #define rep(i,j,k) for (int i = j; i <= k; i++) #define per(i,j,k) for (int i = j; i >= k; i--) #define loop(i,j,k) for (int i = j;i != -1; i = k[i]) #define lson x << 1, l, mid #define rson x << 1 | 1, mid + 1, r #define ff first #define ss second #define mp(i,j) make_pair(i,j) #define pb push_back #define pii pair<int,int> #define in(x) scanf("%d", &x); using namespace std; typedef long long LL; const int low(int x) { return x&-x; } const double eps = 1e-8; const int INF = 0x7FFFFFFF; const int mod = 1e9 + 7; const int N = 1e5 + 10; int n, a[N]; LL dp[N]; void init() { dp[0] = dp[1] = 0; rep(i, 2, N - 1) dp[i] = dp[i - 1] + 2 * i - 3; } int check(int x) { int now = 1; rep(i, 1, n) { if (now == a[x]) ++now; if (i == x) continue; if (a[i] && a[i] != now) return 0; ++now; } return 1; } int check(int x, int y) { if (x != y - 1) return 0; rep(i, 1, n) { if (i == x || i == y) continue; if (a[i] && a[i] != i) return 0; } return 1; } LL get(int x, int y) { int l = n + 1, r = 0; rep(i, 1, n) if (a[i] && a[i] != i) l = min(l, i), r = max(r, i); rep(i, l, r) if (a[i] && a[i] == i) return 0; int L = l, R = n - r + 1; per(i, l, 1) if (a[i] == i) { L = l - i; break; } rep(i, r, n) if (a[i] == i) { R = i - r; break; } return 1LL * (L - x) * (R - y); } LL get() { LL res = 0, now = 0; rep(i, 1, n) if (a[i]) res += dp[a[i] - now - 1], now = a[i]; return res + dp[n - now]; } int main() { init(); while (scanf("%d", &n) != EOF) { int flag = 0, l = 0, r = 0; rep(i, 1, n) { scanf("%d", &a[i]); if (!a[i]) continue; if (abs(a[i] - i) > 1) flag = i; if (a[i] - i == 1) l = i; if (i - a[i] == 1) r = i; } if (flag) { printf("%d\n", check(flag)); continue; } if (l&&r) { printf("%d\n", check(l, r)); continue; } if (l || r) { printf("%lld\n", get(!l, !r)); continue; } printf("%lld\n", get()); } return 0; }