bzoj3100 排列

  这个题有点恶心啊,总觉得以前做过,可就是想不出来。最后在发现是meng神出的一次模拟赛题= =当时我还P都不会呢...

  这个题有两个关键点,一个是选取的子序列内不能有重复,二是选取的子序列要是一个排列(当然这包含了一)。首先如果确定了不会重复,那么只要子序列内的数的和等于(len+1)*len/2就行了,那么怎么确定不重复呢?

  我们可以O(N)时间求出来每个数两边第一个和它相同的数的位置(L,R),这样我们可以不断地更新合法的序列的范围。

  具体方案:我们先确定每一个1的位置(因为每个区间首先必须有1),按照这个来划分区间。我们先从左到右扫每个区间,相当于枚举了右端点k(我们要包含那个1),同时记录最大值mx,作为更新答案的标准(因为最大值同时是排列的长度)。然后我们只要保证左端点是合法的就行了。我们不断地用每个数的L来更新左边界(左端点的最小值),这样只要保证k-mx+1在大于左边界就行了,再用前缀和判定是不是排列就行了。

  这个题被空间卡的好惨,贴一个没优化空间的代码吧。

  

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 #define maxn 1200000
 7 using namespace std;
 8 int l[maxn],r[maxn],s[maxn],a[maxn],last[maxn],st[maxn];
 9 int n,m;
10 
11 int main()
12 {
13     //freopen("perm.in","r",stdin);
14     scanf("%d",&n);
15     for (int i=1;i<=n;i++) 
16     {
17         scanf("%d",&a[i]);
18         s[i]=s[i-1]+a[i];
19         if (a[i]==1) st[++m]=i;
20     }
21     st[0]=0,st[m+1]=n+1;
22     for (int i=1;i<=n;i++) 
23     {
24         l[i]=last[a[i]];
25         last[a[i]]=i;
26     }
27     for (int i=1;i<=n;i++) last[i]=n+1;
28     for (int i=n;i;i--)
29     {
30         r[i]=last[a[i]];
31         last[a[i]]=i;
32     }
33     int ans=0;
34     for (int i=1;i<=m;i++)
35     {
36         int j=st[i];//枚举1
37         int mx=0,rr=n+1;
38         for (int k=j;k>=st[i-1]+1;k--)//向左扫,确定左端点
39         {
40             mx=max(mx,a[k]);//区间中最大值(同时也是答案长度)
41             rr=min(rr,r[k]-1);//区间右端点最大值
42             if (k+mx-1<=rr&&s[k+mx-1]-s[k-1]==(mx+1)*mx/2) ans=max(ans,mx);
43         }
44     }
45     for (int i=m;i;i--)
46     {
47         int j=st[i];//枚举1
48         int mx=0,ll=0;
49         for (int k=j;k<=st[i+1]-1;k++)//向右扫,确定右端点
50         {
51             mx=max(mx,a[k]);//区间中最大值(同时也是答案长度)
52             ll=max(ll,l[k]+1);//区间中左端点最大值
53             if (k-mx+1>=ll&&s[k]-s[k-mx]==(mx+1)*mx/2) ans=max(ans,mx);
54         }
55     }
56     printf("%d\n",ans);
57     return 0;
58 }
P

 

转载于:https://www.cnblogs.com/zig-zag/archive/2013/05/16/3082492.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值