思路是预处理出每个人去前边和后面的沿途不高兴和,然后对他们的差从小到大排序一下,枚举前面的保密室的最终人数,然后求个前缀和和后缀和。能过样例,但是只有14分。除了数据范围(可能爆long long)还有什么地方不太对吗?求调。
#include<bits/stdc++.h>
using namespace std;
#define ls (nw<<1)
#define rs ((nw<<1)|1)
#define mid ((l+r)>>1)
typedef long long ll;
const ll N=1e5+10,M=6e5+10;
ll n,t[N],m,A,B,ans=1e18,sum,pres[M],sufs[M];
struct stu{
ll pre,suf,bh;
}s[M];
bool a[N][7];
bool cmp(const stu &a1,const stu &a2){
return a1.suf-a1.pre>a2.suf-a2.pre;
}
void build_tree(ll nw,ll l,ll r){
if(l==r){
t[nw]=2;
return;
}
build_tree(ls,l,mid);
build_tree(rs,mid+1,r);
t[nw]=t[ls]+t[rs];
}
void update(ll nw,ll l,ll r,ll x){
if(l==r){
t[nw]--;
return;
}
if(x<=mid){
update(ls,l,mid,x);
}
else{
update(rs,mid+1,r,x);
}
t[nw]=t[ls]+t[rs];
}
ll query(ll nw,ll l,ll r,ll x,ll y){
if(x<=l&&y>=r){
return t[nw];
}
ll ans=0;
if(x<=mid){
ans+=query(ls,l,mid,x,y);
}
if(y>mid){
ans+=query(rs,mid+1,r,x,y);
}
return ans;
}
signed main(){
scanf("%lld %lld %lld %lld",&n,&m,&A,&B);
build_tree(1,1,n);
for(int i=1;i<=n;i++){
for(int j=1;j<=6;j++){
a[i][j]=1;
}
}
for(int i=1;i<=m;i++){
ll x,sum=0,y;char c;
scanf("%lld%c",&x,&c);
y=ll(c-'A'+1);
if(y==1) sum+=a[x][2];
if(y==6) sum+=a[x][5];
a[x][y]=0;
if(y==3||y==4) update(1,1,n,x);
s[i].pre=sum+query(1,1,n,1,x);
s[i].suf=sum+query(1,1,n,x,n);
s[i].bh=i;
}
sort(s+1,s+m+1,cmp);
for(int i=1;i<=m;i++){
pres[i]=pres[i-1]+s[i].pre;
}
for(int i=m+1;i>=1;i--){
sufs[i]=sufs[i+1]+s[i].suf;
}
for(int i=0;i<=m;i++){
sum=(i*(i-1)/2+(m-i)*(m-i-1)/2)*B+(pres[i]+sufs[i+1])*A;
ans=min(ans,sum);
}
printf("%lld\n",ans);
}