正题
题目链接:https://www.luogu.org/problem/P3470
题目大意
一个+−+-+−序列,+++表示111,−-−表示−1-1−1。sis_isi表示到第iii个的前缀和,要求
q+sn=p且q+si≥0(i∈[1..n])q+s_n=p且q+s_i\geq 0(i\in[1..n])q+sn=p且q+si≥0(i∈[1..n])
然后有以下操作
- 消耗xxx将一位取反
- 消耗yyy将最后一位丢到最前面
求最小消耗
解题思路
先不考虑操作2
若已经满足了q+sn=pq+s_n=pq+sn=p,那么答案显然为max{−q−si,0}∗2∗xmax\{-q-s_i,0\}*2*xmax{−q−si,0}∗2∗x
然后我们考虑那若不同呢,我们考虑将两个可以合并在一起计算
答案就是(max{−q−si,0}2+∣max{−q−si,0}−(q−p+sn)2∣)∗x(\frac{max\{-q-s_i,0\}}{2}+|\frac{max\{-q-s_i,0\}-(q-p+s_n)}{2}|)*x(2max{−q−si,0}+∣2max{−q−si,0}−(q−p+sn)∣)∗x
上的都可以O(1)O(1)O(1)的时间内计算,所以我们暴力枚举转了多少次
这里的max{−q−si,0}max\{-q-s_i,0\}max{−q−si,0}我是用线段树进行维护的,时间复杂度O(nlogn)O(n\log n)O(nlogn)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e6+100;
struct Tree_node{int l,r,w,lazy;
};
int n,p,q,x,y,s[N],k,z,ans;
char a[N];
struct Seg_Tree{Tree_node t[N*4];void Build(int x,int l,int r){t[x].l=l;t[x].r=r;if(l==r){t[x].w=s[l];return;}int mid=(l+r)/2;Build(x*2,l,mid);Build(x*2+1,mid+1,r);t[x].w=max(t[x*2].w,t[x*2+1].w);}void DownData(int x){if(!t[x].lazy) return;t[x*2].lazy+=t[x].lazy;t[x*2+1].lazy+=t[x].lazy;t[x*2].w+=t[x].lazy;t[x*2+1].w+=t[x].lazy;t[x].lazy=0;return;}void Change(int x,int l,int r,int val){if(t[x].l==l&&t[x].r==r){t[x].w+=val;t[x].lazy+=val;return;}DownData(x);int mid=(t[x].l+t[x].r)/2;if(r<=mid) Change(x*2,l,r,val);else if(l>mid) Change(x*2+1,l,r,val);else Change(x*2,l,mid,val),Change(x*2+1,mid+1,r,val);}
}Tree;
int main()
{freopen("data.in","r",stdin);freopen("data.out","w",stdout);scanf("%d%d%d%d%d",&n,&p,&q,&x,&y);scanf("%s",a+1);n=strlen(a+1);for(int i=1;i<=n;i++)s[i]=s[i-1]+(a[i]=='+'?-1:1);Tree.Build(1,1,n);k=(q-p+s[n])/2;ans=2e9;for(int i=0;i<n;i++){if(i*y>ans) break;if(k>=0) ans=min(ans,i*y+max(Tree.t[1].w-p-k*2,0)*2*x+abs(k)*x);else ans=min(ans,i*y+max(Tree.t[1].w-p,0)*2*x+max(abs(k)-max(Tree.t[1].w-p,0),0)*x);Tree.Change(1,n-i,n-i,-s[n-i]-z);Tree.Change(1,1,n,(a[n-i]=='+'?-1:1));z+=(a[n-i]=='+'?-1:1);}printf("%d",ans);
}