UVA 1471 Defense Lines (高效算法分析序列性质)*

After the last war devastated your country, you - as the king of the land of Ardenia - decided it was high time to improve the defense of your capital city. A part of your fortification is a line of mage towers, starting near the city and continuing to the northern woods. Your advisors determined that the quality of the defense depended only on one factor: the length of a longest contiguous tower sequence of increasing heights. (They gave you a lengthy explanation, but the only thing you understood was that it had something to do with firing energy bolts at enemy forces). After some hard negotiations, it appeared that building new towers is out of question. Mages of Ardenia have agreed to demolish some of their towers, though. You may demolish arbitrary number of towers, but the mages enforced one condition: these towers have to be consecutive. For example, if the heights of towers were, respectively, 5, 3, 4, 9, 2, 8, 6, 7, 1, then by demolishing towers of heights 9, 2, and 8, the longest increasing sequence of consecutive towers is 3, 4, 6, 7.
Input The input contains several test cases. The first line of the input contains a positive integer Z ≤ 25, denoting the number of test cases. Then Z test cases follow, each conforming to the format described below. The input instance consists of two lines. The first one contains one positive integer n ≤ 2 · 105 denoting the number of towers. The second line contains n positive integers not larger than 109 separated by single spaces being the heights of the towers.
Output
For each test case, your program has to write an output conforming to the format described below. You should output one line containing the length of a longest increasing sequence of consecutive towers, achievable by demolishing some consecutive towers or no tower at all.
Sample Input
2 9 5 3 4 9 2 8 6 7 1 7 1 2 3 10 4 5 6
Output
4 6

 

#include<iostream>
#include<algorithm>
#include<string>
#include<map>//int dx[4]={0,0,-1,1};int dy[4]={-1,1,0,0};
#include<set>//int gcd(int a,int b){return b?gcd(b,a%b):a;}
#include<vector>
#include<cmath>
#include<stack>
#include<string.h>
#include<stdlib.h>
#include<cstdio>
#define mod 1e9+7
#define ll long long
#define maxn 200005
#define ms memset
using namespace std;
#pragma comment(linker, "/STACK:1024000000,1024000000") ///在c++中是防止暴栈用的

int n,seq[maxn];

int f[maxn] , g[maxn];///枚举g[i]在f[j]中进行二分查找
///f数组记录以第i个元素为结尾的最长连续递增子序列,g数组记录以第j个元素为开始的最长递增子序列。
/*
题目大意:给定连续的序列,删除连续的子序列,
使得剩下的有一个长度最大的连续递增子序列,求最大长度。

时间复杂度紫书上分析过,
首先简单分析:枚举i和j,然后计数,复杂度三次方,
稍微好一点的:预处理出f和g数组,f数组代表前缀和,g代表后缀和。
复杂度二次方。

那么,如何降到nlogn呢?
首先对于(seq[i],f[i])这样的二元组,
肯定要放在类似优先队列中且支持任意删除和增长,
选用set结构,还支持下界和上界查询。

那么什么是不需要的二元组呢?
比如5,3,4,9,2,8,6,7,1
二元组(5,1)先入,
(3,1)再入,选择插入位置,
然后(4,2)再入,此时思考(5,1),
因为是下界lower_bound所以
可以保证在5和4位置之间没有出现比4大的值。
如果在以后有个最优答案需要二元组(5,1)组成,
那么一定可以用(4,2)替换,
筛选的本质在这里。

*/
struct node
{
    int sv,len;
    node(int x=0,int y=0)
    {
        sv=x;
        len=y;
        ///sv=len=0;
    }
    bool operator<(const node& x) const
    {
        return sv<x.sv|| (sv==x.sv && len<x.len) ;
    }
};

set<node> st;

int main()
{
    int t;scanf("%d",&t);
    while(t--)
    {
        st.clear();
        scanf("%d",&n);
        for(int i=0;i<n;i++) scanf("%d",&seq[i]);

        f[0]=1;
        for(int i=1;i<n;i++)
        {
            if(seq[i]>seq[i-1])   f[i]=f[i-1]+1;
            else   f[i]=1;
        }

        g[n-1]=1;
        for(int i=n-2;i>=0;i--)
        {
            if(seq[i]<seq[i+1]) g[i]=g[i+1]+1;
            else g[i]=1;
        }

        int maxx=-1;///答案可选集中没有0这个存在
        st.insert(node(seq[0],f[0]));
        for(int i=1;i<n;i++)
        {
            node now(seq[i],f[i]),t;
            set<node> ::iterator it=st.lower_bound(now);
            if(it!=st.begin())
            {
                t=*(--it);
                maxx=max(maxx,g[i]+t.len);
                it++;
                if(it->sv==seq[i]) continue;
                else if((--it)->len>f[i]) continue;
                else
                {
                    it++;
                    while(it!=st.end() && it->len<=f[i])
                        st.erase(it++);
                    st.insert(now);
                }
            }
            else if(it->len<=f[i])
            {
                st.erase(it);
                st.insert(now);
            }
        }
        cout<<( maxx>0?maxx:1 )<<endl;
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值