hdu 3415"Max Sum of Max-K-sub-sequence"(单调队列)

传送门

题意:

  给出一个有 N 个数字([-1000 , 1000],N ≤ 105)的环状序列;

  让你求一个和最大的连续子序列,并记录起始点。

  要求这个连续子序列的长度小于等于K,加和相同的不同区间,输出起点最小的那组答案。

题解:

  因为序列是环状的,所以可以在序列后面复制一段(或者复制前k - 1个数字)。

  如果用sum[ i ]来表示复制过后的序列的前 i 个数的和;

  那么任意一个子序列[ i..j ]的和就等于s[ j ]-s[ i-1 ]。

  对于每一个 j,用s[ j ]减去最小的一个s[ i ](i ≥ j-k+1)就可以得到以 j 为终点长度不大于k的和最大的序列了。

  将原问题转化为这样一个问题后,就可以用单调队列解决了。

AC代码:

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<deque>
 4 #include<cstdio>
 5 #include<cstring>
 6 using namespace std;
 7 #define INF 0x3f3f3f3f
 8 #define pii pair<int ,int >
 9 const int maxn=2e5+50;
10 
11 int n,k;
12 int a[maxn];
13 int sum[maxn];
14 deque<int >deq;
15 
16 void Solve()
17 {
18     sum[0]=0;
19     for(int i=1;i < 2*n;++i)
20         sum[i]=sum[i-1]+a[i];
21     while(!deq.empty())
22         deq.pop_back();
23     deq.push_back(0);
24 
25     int ans=-INF;
26     int ansL,ansR;
27     for(int i=1;i < 2*n;++i)
28     {
29         while(!deq.empty() && deq.front() < i-k)
30             deq.pop_front();///维护[i-k,i-k+1,...,i-1]的最小值
31         int curSum=sum[i]-sum[deq.front()];
32         if(curSum > ans)
33         {
34             ans=curSum;
35             ansL=deq.front()+1;
36             ansR=i;
37         }
38         while(!deq.empty() && sum[deq.back()] >= sum[i])
39             deq.pop_back();
40         deq.push_back(i);
41     }
42     if(ansR > n)
43         ansR -= n;
44     printf("%d %d %d
",ans,ansL,ansR);
45 }
46 int main()
47 {
48 //    freopen("C:\Users\hyacinthLJP\Desktop\in&&out\contest","r",stdin);
49     int test;
50     while(~scanf("%d",&test))
51     {
52         while(test--)
53         {
54             scanf("%d%d",&n,&k);
55             for(int i=1;i <= n;++i)
56             {
57                 scanf("%d",a+i);
58                 a[n+i]=a[i];
59             }
60             Solve();
61         }
62     }
63     return 0;
64 }
View Code
原文地址:https://www.cnblogs.com/violet-acmer/p/10792796.html