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
9
1
HINT
Source
思路:很显然,要形成最长长度为n-1的上升序列,就是要将原来的自然序列中的某一个数,插入到相对于原来位置的其他位置中去,可以再最前面和最后面,这样就能保证最长上升子序列长度为n-1,,对于输入的a数组分情况讨论,a[i] = 0不影响,分的情况不讨论a[i] = 0的情况
对于自然序列1 2 3 4 .... i i+1.... i+j......n
形成n-1的长度可以转换为
1 2 3 ..... i+j i i+1 ... i+j-1....n
或者1 2 3 ......i+1 i+2......i+j i......n
其中有一段子段左移一位或者右移一位
(1)对于所有 a[i] == i
这种情况只需要对连续的0作为一个子串进行变换,很容易得出结果
(2)a[i] != i && abs(a[i] - i) > 1
由于是将某一个数插入到某个位置,那么如果存在abs(a[i] - i) > 1那么一定是由某个数的插入的到这个位置而不是某个子段移动得到
(3) 只剩下a[i] != i&& abs(a[i] - i) = 1的情况
这种情况有可能是长度为1的子段固定移动而来,对这种情况进行特殊判断
剩下的只可能某个连续的子段移动而来,计算这个结果即可
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<string>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<algorithm>
#include<iostream>
const int maxn = 1e5 + 10;
typedef long long ll;
using namespace std;
ll a[maxn], b[maxn], n;
int main()
{
while(scanf("%lld", &n) != EOF) {
ll s1 = 0, s2 = 0, ind = -1;
ll f = 1;
for(ll i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
b[i] = a[i];
if(!a[i] || a[i] - i == 0) continue;
ll c = a[i] - i;
f = 0;
if(ind < 0) ind = i;
if(abs(c) > 1) ind = i;
}
if(!f) {
if(abs(a[ind] - ind) > 1) {
ll sign = 1, ty = a[ind];
//恢复自然序列
if(a[ind] < ind) {
for(ll i = ind; i > a[ind]; i--)
b[i] = b[i - 1];
b[ty] = ty;
} else {
for(ll i = ind; i < a[ind]; i++)
b[i] = b[i + 1];
b[ty] = ty;
}
for(ll i = 1; i <= n; i++) {
if(!b[i]) continue;
if(b[i] != i) sign = 0;
}
printf("%lld\n", sign);
} else {
//固定交换而来
if(ind < n && a[ind + 1] && (a[ind] - ind) * (a[ind + 1] - ind - 1) == -1) {
ll sign = 1;
swap(b[ind], b[ind + 1]);
for(ll i = 1; i <= n; i++) {
if(!b[i]) continue;
if(b[i] != i) sign = 0;
}
printf("%lld\n", sign);
} else {
ll sign = 1, sum = 0;
ll id1 = ind, id2 = ind; //两个边界
for( ; id1 >= 1 && a[id1] != id1; id1--);
for( ; id2 <= n && a[id2] != id2; id2++);
for(ll i = id1 - 1; i >= 1; i--) {
if(!a[i]) continue;
if(a[i] != i) sign = 0;
}
for(ll i = id2 + 1; i <= n; i++) {
if(!a[i]) continue;
if(a[i] != i) sign = 0;
}
if(sign) {
ll idx = ind;
//ind ~ idx整体移动的序列的长度
for(ll i = ind; i <= n; i++)
if(a[i] - i == a[ind] - ind) idx = i;
if(a[ind] < ind) {
sum = (id2 - idx) * (ind - id1 - 1);
} else {
sum = (ind - id1) * (id2 - idx - 1);
}
}
printf("%lld\n", sum);
}
}
} else {
ll d = 1, sum = 0;
while(d < n) {
if(a[d]) { d++; continue; }
ll i = d;
for( ; i <= n && !a[i]; i++);
i--;
sum += (i - d) * (i - d);
d = i + 1;
}
printf("%lld\n", sum);
}
}
return 0;
}