CSU 1807: 最长上升子序列~

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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值