链表+思维+模拟——NWERC 2019 Jackdaws And Crows


给定一个数组a,将其变成正负相间
操作1:任选一个子集,里面的数-1或+1,代价c
操作2:删掉一个数,代价r
重要结论:如果ab符号冲突,bc符号冲突,那么肯定要删两个(删一个不够,自己模拟一下情况)
从小到大枚举a[i],表示abs(a[i])小于a[i]的都可以变成自由点
同时非自由点需要删掉的个数可以O(1)进行维护

pass:0的情况特别恶心。。但是只有两种策略:把所有0都删掉,把所有0变成1,-1(不可能存在删一些0,改一些0的情况),讨论一下就行


#include<bits/stdc++.h>
using namespace std;
#define N 500005
#define ll long long

ll n,c,R,l[N],r[N],a[N];

struct Node{ll x,id;}s[N]; 
int cmp(Node a,Node b){return abs(a.x)<abs(b.x);}

int f(int i,int j){
    if(i==0 || i==n+1 || j==0 || j==n+1)return 0;
    if(a[i]==0 || a[j]==0)return 1;
    int x=(j-i)%2,y;
    if(a[i]*a[j]>0)y=1;
    else y=-1;
    if(x==1 && y==-1)return 0;
    if(x==0 && y==1)return 0;
    return 1;
}

int main(){
    cin>>n>>c>>R;
    for(int i=1;i<=n;i++)
        scanf("%lld",&s[i].x),s[i].id=i;
    for(int i=1;i<=n;i++)a[i]=s[i].x;
    sort(s+1,s+1+n,cmp);
    ll now=0,ans=0x3f3f3f3f3f3f3f3f;
    for(int i=0;i<=n;i++)r[i]=i+1;
    for(int i=1;i<=n+1;i++)l[i]=i-1;
    r[n+1]=n+1;l[0]=0;
    
    //把0全删掉
    int k=1;
    while(a[k]==0 && k<=n){
        now+=R;k++;
    }
    for(int i=k+1;i<=n;i++){
        if(a[i]==0)now+=R;
        else if((a[i]>0)==(a[k]>0))//相邻两数同号 
            now+=R;
        else k=i;
    }
    ans=now;
    
    //把0全变成自由点
    now=0;int last=0;
    for(int i=1;i<=n;i++)if(a[i]){
        if(last)now+=f(last,i)*R;
        last=i;
    }else {
        r[l[i]]=r[i];l[r[i]]=l[i];
    }
    ans=min(ans,now+c);
    
    
    for(int i=1;i<=n;i++)if(s[i].x!=0){//把s[i]变成自由点 
        int pos=s[i].id;
        now-=(f(l[pos],pos)+f(pos,r[pos])-f(l[pos],r[pos]))*R; 
        r[l[pos]]=r[pos];l[r[pos]]=l[pos];
        ans=min(ans,now+c*(abs(s[i].x)+1));    
    }
    cout<<ans<<'
';
}
原文地址:https://www.cnblogs.com/zsben991126/p/12889767.html