【bzoj2151】種樹

小哈里發表於2018-04-24

題面

傳送門
傳送門2

題解

如果沒有相鄰限制的話,我們開一個大根堆每一次取最大的就行了,但是如果存在限制,我們就加入一個後悔操作,來做調整貪心。
首先如果我們選擇了一個點i,那麼其相鄰的點i−1,i+1,都不能選了,所以我們刪除這兩個點,因為i與它們兩個是互斥的。
所以我們加入後悔操作的時候,是用兩者之和減去a[i],即我們每選擇一個點,我們就加入一個新節點 node(i,a[i+1]+a[i−1]−a[i]),這樣就可以做到滿足限制條件下的調整了,另外就是維護位置,可以連結串列直接 O(1)做,或者set維護一下也可。

1、連結串列寫法
#include<iostream>
#include<queue>
using namespace std;
const int maxn = 200005;

struct node{
    int id, val;
    node(int id, int val):id(id),val(val){}
    bool operator < (const node &b)const{return val<b.val;}
};
priority_queue<node>q;

int vis[maxn], pre[maxn], nxt[maxn];
void change(int x){
    vis[x] = 1;  //vis[i]==1,不在連結串列裡的點。
    nxt[pre[x]] = nxt[x];
    pre[nxt[x]] = pre[x];
    pre[x] = 0; nxt[x] = 0;
}

int main(){
    int n, m, a[maxn];
    cin>>n>>m;
    if(n<2*m){cout<<"Error!\n"; return 0;}
    for(int i = 1; i <= n; i++){ cin>>a[i]; q.push(node(i,a[i])); }
    for(int i = 2; i <= n; i++)pre[i] = i-1; pre[1]=n;
    for(int i = 1; i < n; i++)nxt[i] = i+1; nxt[n]=1;

    long long ans = 0;
    for(int i = 1; i <= m; i++){
        while(vis[q.top().id])q.pop();
        node t = q.top();  q.pop();
        ans += t.val;

        int l = pre[t.id], r = nxt[t.id];
        change(l);  change(r);

        a[t.id] = a[l]+a[r]-a[t.id];
        q.push(node(t.id,a[t.id]));//反悔節點
    }
    cout<<ans<<'\n';
    return 0;
}
2、STL寫法
#include<iostream>
#include<queue>
#include<set>
using namespace std;
const int maxn = 200005;

int n, m, a[maxn];
struct node{
    int id, val;
    node(int id, int val):id(id),val(val){}
    bool operator < (const node &b)const{return val<b.val; }
};
struct data{
    int id, val;
    data(int id, int val):id(id),val(val){}
    bool operator < (const data &b)const{return id<b.id; }
};
priority_queue<node>q;
set<data>s;
set<data>::iterator pre,nxt;

int main(){
    cin>>n>>m;
    if(n<2*m){cout<<"Error!\n";return 0;}
    for(int i = 1; i <= n; i++){
        cin>>a[i];  q.push(node(i,a[i]));
        s.insert(data(i,a[i]));
    }

    long long ans = 0;
    for(int i = 1; i <= m; i++){
        node t = q.top();  q.pop();
        while(!s.empty() && s.find(data(t.id,t.val))==s.end())
            { t = q.top();  q.pop();}

        ans += t.val;
        if(i == m)break;
        data now = data(t.id,t.val);
        nxt = s.upper_bound(now);
        pre = s.lower_bound(now);
        if(nxt == s.end())nxt = s.begin();
        if(pre == s.begin())pre = --s.end();
        else --pre;

        data to = data(t.id,pre->val+nxt->val-t.val);
        s.erase(s.find(now));
        s.erase(pre);
        s.erase(nxt);
        s.insert(to);

        q.push(node(to.id,to.val));
    }
    cout<<ans<<"\n";
    return 0;
}
3、最後

垃圾樣例怎麼改都是對的。

相關文章