CSU 1807 最长上升子序列~

来源:互联网 发布:139端口基于什么协议 编辑:程序博客网 时间:2024/05/29 15:10
[Submit][Status][Web Board]

Description

Bobo 在 ICPCCamp 学会了解决最长上升子序列问题后得到了一个长度为 n 的数列 p1,p2,…,pn.
Bobo 想用 1,2,…,n 来替换其中值为 0 的元素,使得 p1,p2,…,pn 互不相同(即 p1,p2,…,pn 是 {1,2,…,n} 的排列)。
现在 Bobo 想知道,替换后最长上升子序列的长度恰好为 (n-1) 数列的数量。

Input

输入包含不超过 300 组数据,其中不超过 20 组的 n 超过 100.
每组数据的第一行包含一个整数 n (1≤n≤105).
第二行包含 n 个整数p1,p2,…,pn  (0≤pi≤n).
保证p1,p2,…,pn中非 0 的元素互不相同。

Output

对于每组数据,输出一个整数表示要求的值。

Sample Input

30 0 040 0 0 051 0 0 4 5

Sample Output

49

1

先递推出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;}


0 0