给定一个数组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<<' '; }