XTU 1264

2017江苏省赛的E题,当时在场上看错了题目没做出来,现在补一下……

题目链接:http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1264(注意在上面使用G++编译的话,请使用%I64d)

Time Limit : 3000 MS

Memory Limit : 65536 KB

Bobo has a integer sequence a1,a2,,an of length n. Each time, he selects two ends 0l<rn and add ∑(i=l+1 to r)(ai) − C into a counter which is zero initially. He repeats the selection for at most m times.

If each end can be selected at most once (either as left or right)(所有端点只能使用一次), find out the maximum sum Bobo may have.

Input

The input contains zero or more test cases and is terminated by end-of-file. For each test case:

The first line contains three integers nmC. The second line contains n integers a1,a2,,an.

  • 2n1e5
  • 12mn+1
  • |ai|,C1e4
  • The sum of n does not exceed 1e6.

Output

For each test cases, output an integer which denotes the maximum.

Sample Input

4 1 1
-1 2 2 -1
4 2 1
-1 2 2 -1
4 2 2
-1 2 2 -1
4 2 10
-1 2 2 -1

Sample Output

3
4
2
0

题目思路:

既然是让我们求最大子序列和,首先想到的是O(n)的算法,所以最简单的思路是O(n)算出最大连续子序列和,然后标记掉两个用过的端点,再继续O(n)的算这时的最大连续子列和……

反复如此,直到某个时候,这个子序列和已经比C小了 或者 次数已经到达m次,就停止。

显然,这样的时间复杂度可以到达O(mn),明显是超时,显然,m次的查询很难再减少了,就考虑怎么降低寻找最大连续子列和的时间复杂度。

这时,可以考虑用前缀和保存整个数组,然后再对这个sum[0……n]进行排序(注意,此时的sum[i]有n+1个,因为 0 <= l < r <= n)

那么,我们每次想查询最大连续子列和,只需要,这个sum[0……n]数组的最后一个(sum[r])减去最前一个(sum[l]),不断地l++,r--即可……

这样的时间复杂度最坏大概是O(n)+O(nlgn)+O(m),就不会超时。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define MAXN 100000+5
 4 using namespace std;
 5 int n,m,c,cnt,sum[MAXN];
 6 long long ans; 
 7 int main()
 8 {
 9     while(scanf("%d%d%d",&n,&m,&c)!=EOF)
10     {
11         sum[0]=0;
12         for(int i=1;i<=n;i++)
13         {
14             scanf("%d",&cnt);
15             sum[i]=sum[i-1]+cnt;
16         }
17         sort(sum,sum+n+1);
18         ans=0;cnt=0;
19         for(int l=0,r=n;l<r;l++,r--)
20         {
21             if(sum[r]-sum[l]-c>0)
22             {
23                 ans+=sum[r]-sum[l]-c;
24                 cnt++;
25             }
26             if(cnt>=m || sum[r]-sum[l]-c<=0) break;
27         }
28         printf("%I64d
",ans);
29     }
30 }

其实,我们不难发现,前缀和也可以求出最大连续子列和,但是如果我们只要查询一次最大连续子列和,显然是遍历a[1……n]数组的O(n)的算法比较快,但如果我们要像上题那样,不断地求第二大、第三大……这样的,还是用前缀和维护数组比较快。

附上2017湘潭邀请赛暨2017江苏省赛(JSCPC)的题解:http://files.cnblogs.com/files/dilthey/xiangtan-2017-solution.pdf

比赛题目地址:http://202.197.224.59/OnlineJudge2/index.php/Contest/problems/contest_id/43

原文地址:https://www.cnblogs.com/dilthey/p/6876693.html