比较夸张的贪心…
题目大意:
给定一个长度为n的序列,选出m段不相交,不相邻的子序列,使其和最大
数据范围
n<=6000000 |
a
i
a_i
ai|<=100000000
m<=n/2
代码细节特别多啊…
考虑把相邻所有正元素和负元素合并
如果正元素个数>m是不太好处理的
考虑贪心
假设我们选了全部正元素
我们现在要么是舍弃正元素,要么是用负元素把两个正元素合起来
这样的花费都是
∣
a
i
∣
|a_i|
∣ai∣的
这样的花我们只要用优先队列和链表维护序列就可以做到
n
l
o
g
n
nlogn
nlogn了
考虑优化这个贪心
如果当前我们要选
k
k
k个数
那我们找到原序列中第
k
k
k大和第
3
k
3k
3k大的位置
设第
k
k
k大的数位
m
i
d
1
mid1
mid1第
3
k
3k
3k大的数为
m
i
d
2
mid2
mid2
我们直接把
∣
a
i
∣
<
=
m
i
d
1
|a_i|<=mid1
∣ai∣<=mid1的合并
把
∣
a
i
∣
>
=
m
i
d
2
|a_i|>=mid2
∣ai∣>=mid2在接下来的步骤不考虑
我们可以证明下面一种情况所有被舍弃的元素是不可能被选中合并的
同时我们也可以证明前半部分我们选取的必定是在我们用优先队列时也选的
即使在最不优的情况下,我们一次至少选取
k
3
\frac{k}{3}
3k个元素
这样问题规模就会缩小到
2
3
\frac{2}{3}
32
单词操作是
O
(
n
)
O(n)
O(n)的
故总时间复杂度为
O
(
n
)
+
O
(
2
3
n
)
+
O
(
(
2
3
)
2
n
)
+
O
(
(
2
3
)
3
n
)
+
.
.
.
.
.
.
.
O(n)+O(\frac{2}{3}n)+O( (\frac{2}{3})^2 n )+O( (\frac{2}{3})^3 n )+.......
O(n)+O(32n)+O((32)2n)+O((32)3n)+.......
=
O
(
n
)
=O(n)
=O(n)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
const ll INF = 1e16;
int n , m , tot , a[6010000] , tott , en , k;
ll tmp[6010000];
ll cnt , sum;
struct Now{
ll data;
int pl;
Now(ll _data=0, int _pl=0)
{
data = _data;
pl = _pl;
}
bool operator <(const Now &a)const
{
return data < a.data || (data == a.data && pl < a.pl);
}
bool operator <=(const Now &a)const
{
return data < a.data || (data == a.data && pl == a.pl);
}
}b[6010000];
struct node{
int n,l,pl;
int n2,l2;
ll data;
}e[6010000];
void read(int &sum)
{
sum = 0;char c = getchar();bool flag = true;
while( c < '0' || c > '9' ) {if(c == '-') flag = false;c = getchar();}
while( c >= '0' && c <= '9' ) sum = sum * 10 + c - 48 , c = getchar();
if(!flag) sum = -sum;
}
queue<int>q;
int rest;
bool flag1[3010000];
bool flag2[3010000];
void delee(int x)
{
if(x == en || x == 0) return;
if(flag2[x]) return;
flag2[x] = true;
e[e[x].l2].n2 = e[x].n2;
e[e[x].n2].l2 = e[x].l2;
}
void dele(int x)
{
if(x == en || x == 0) return;
if(flag1[x]) return;
rest--;
flag1[x] = true;
e[e[x].l].n = e[x].n;
e[e[x].n].l = e[x].l;
e[x].l = e[x].n = e[x].data = 0;
delee(x);
}
bool vis[3010000];
Now mid1,mid2;
void Push(int x)
{
if(x != 0 && x != en && !vis[x] && Now(abs(e[x].data) , x) <= mid1)
q.push(x) , vis[x] = true;
}
int cas;
void work()
{
e[0].n = e[0].n2 = 1;
rep(i,1,tot) e[i].l = e[i].l2 = i-1,e[i].n = e[i].n2 = i+1;
en = e[tot].n;
e[en].l = e[en].l2 =tot;
e[0].data = e[en].data = 0;
rest = tot;
while(1)
{
tott = 0;
if(rest <= m*2-1) break;
for(int i = e[0].n2;i != en;i = e[i].n2)
b[++tott].data = abs(e[i].data) , b[tott].pl = i;
k = (rest - (m*2-1))/2;
nth_element(b+1,b+min(k,tott),b+tott+1);
mid1 = b[min(k,tott)];
nth_element(b+1,b+min(3*k,tott),b+tott+1);
mid2 = b[min(3*k,tott)];
if(mid2.data == 0) mid2.data = INF;
for(int i = e[0].n2;i != en;i = e[i].n2)
{
Now tm(abs(e[i].data) , i);
if(mid2 < tm) delee(i);
else Push(i);
}
while(!q.empty())
{
int x=q.front();vis[x] = false;q.pop();
if(flag1[x]) continue;
if(e[x].l > 0 && abs(e[e[x].l].data) < abs(e[x].data) )continue;
if(e[x].n != en && abs(e[e[x].n].data) < abs(e[x].data)) continue;
e[x].data += e[e[x].l].data + e[e[x].n].data;
bool flag = false;
if(e[x].l == 0 || e[x].n == en) flag = true;
dele(e[x].l);dele(e[x].n);
if(flag) dele(x);
else Push(x);
Push(e[x].l);Push(e[x].n);
}
}
ll ans = 0;
for(int i = e[0].n;i;i = e[i].n)
if(e[i].data > 0) ans += e[i].data;
printf("%lld\n",ans);
return;
}
int main()
{
freopen("cut.in","r",stdin);
freopen("cut.out","w",stdout);
read(n);read(m);
rep(i,1,n)
{
read(a[i]);
if(!tot && a[i] < 0) continue;
if(a[i] == 0) continue;
if(tot && tmp[tot] >= 0 && a[i] > 0) tmp[tot] += a[i];
else if(tmp[tot] <= 0 && a[i] < 0) tmp[tot] += a[i];
else tmp[++tot] = a[i];
}
if(tmp[tot] < 0) tot--;
rep(i,1,tot)
if(tmp[i] > 0) cnt++ , sum+=tmp[i];
rep(i,1,tot) e[i].data = tmp[i] , e[i].pl = i;
work();
return 0;
}