专栏文章
P7701
P7701题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miowuwdw
- 此快照首次捕获于
- 2025/12/03 02:27 3 个月前
- 此快照最后确认于
- 2025/12/03 02:27 3 个月前
题面
有 个人分布在 的座位中,这 人按顺序从它的座位走到位于中央的走廊,然后往前或往后走进第一排前或最后一排后的一个房间。设对于一个人,它经过的人数为 ,房间里人数为 ,那么它的不满度为 。最小化所有人不满度的和。
题解
你好。
可以发现按顺序这一点很重要,由此推出对于每个人,它往前往后走所经过的人数总是不变。
所以可以预处理第 个人往前走的代价 ,往后走的代价 。
然后,若上面的房间有 个人,则下面的房间就有 个人,这样的代价为 ,可以用等差数列求和公式直接推出。
于是就可以这样想:若 个人全都去前面的房间,我们要从中抽取 个人去后面的房间,那么如何使代价最小。
从贪心的角度,可以发现选取能省代价最多的 个人即可,于是将 数组和 数组按照 排序。
接着,枚举 ,前 个去后面,后 个去前面,预处理排序后 和 的前缀和,就可以完整地计算代价了。
对于 和 数组的预处理,可以用数据结构进行维护。
代码
CPP#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+2;
ll n,m;
char s[10];
ll A,B,ans;
ll tr[N];
bool mp[N][8];
struct node{
ll a,b;
} p[6*N];
int lowbit(int x) {return x&(-x);}
void add(int x,int val) {
for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=val;
return;
}
ll sum(int x) {
int sum=0;
for(int i=x;i;i-=lowbit(i)) sum+=tr[i];
return sum;
}
bool cmp(node a,node b) {
return (a.a-a.b)>(b.a-b.b);
}
int main() {
scanf("%lld%lld%lld%lld",&n,&m,&A,&B);
for(int i=1;i<=n;i++) {add(i,2);for(int j=1;j<=6;j++) mp[i][j]=true;}
for(int i=1;i<=m;i++) {
cin>>s;
int x=0,y=s[strlen(s)-1]-'A'+1;
for(int i=0;i<strlen(s)-1;i++) x=x*10+s[i]-'0';
if(y==1) p[i].a+=mp[x][2],p[i].b+=mp[x][2];
if(y==6) p[i].a+=mp[x][5],p[i].b+=mp[x][5];
if(y==3||y==4) add(x,-1);
mp[x][y]=false;
p[i].a+=sum(x),p[i].b+=sum(n)-sum(x-1);
}
sort(p+1,p+1+m,cmp);
for(int i=1;i<=m;i++) p[i].a+=p[i-1].a,p[i].b+=p[i-1].b;
ans=1e16;
for(ll i=0;i<=m;i++) ans=min(ans,A*(p[i].b+p[m].a-p[i].a)+B*(i*(i-1ll)/2ll+(m-i-1ll)*(m-i)/2ll));
printf("%lld",ans);
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...