2018.8.19T2(贪心)

比较夸张的贪心…
题目大意:
给定一个长度为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 ∣ &lt; = m i d 1 |a_i|&lt;=mid1 ai<=mid1的合并
∣ a i ∣ &gt; = m i d 2 |a_i|&gt;=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;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值