ICPC2022沈阳|I-Quartz Collection(权值线段树)

题意 :

  • 商店里面有n个种类宝石,分别给出第一次购买和第二次购买的价格,只有购买完第一次后才能用第二次购买的价格,两人都想用最少的成本购买宝石
  • 购买的顺序是 : 爱丽丝先买一块石英,然后鲍勃和爱丽丝轮流买两块,直到只剩下一块。以A代表Alice,B代表Bob,顺序为ABBAA...BBA
  • 然后m次修改操作,修改某一种类宝石第一次购买和第二次购买的价格,每轮修改后输出爱丽丝的最小花费
     

题解思路 :

        假设Alice全以第一次的价格购买n种宝石(显然这是不可能的),对于a[i]<=b[i]的宝石,Alice是赚的,否则Alice是亏的。双方开始轮流选宝石,一开始肯定是选最赚的(也就是(a[i]-b[i]为负数且越小越好))的第一次价格,并且根据赚的程度依次由ABBAABB...挑走。

        选完所有赚的宝石,为了避免选到亏的宝石,Alice和Bob会回头去以第二次价格选对方选过的。

        我们模拟一遍过程可知,假如赚的宝石数量为奇数,那么亏的宝石一定由Bob开始选第一个,并且根据亏的程度由BABABABA...挑走;假如赚的宝石数量为偶数,那么亏的宝石一定由Alice开始选第一个,并且根据亏的程度由ABABABAB...挑走。

       如何求第1,3,5,7....大的数的总和呢?我们可以用权值线段树来维护。

        

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define lson pos<<1
#define rson pos<<1|1
const int N=3e5+10;
const int maxn=1e5;
int n,m;
int a[N],b[N];
//int tree[N<<3];
struct node {
	int sum[4];
	int size;
} tree[N<<3];

void pushup(int pos) {

	tree[pos].size=tree[lson].size+tree[rson].size;

	for(int i=0; i<4; i++) {
		tree[pos].sum[i]=tree[lson].sum[i];
	}
	int ave=tree[lson].size%4;
	for(int i=0; i<4; i++) {
		tree[pos].sum[(i+ave)%4]+=tree[rson].sum[i];
	}
}
void update(int pos, int l, int r, int k, int cnt) { //表示数k的个数多cnt个
	if(l==r) {
		if(cnt==1) {
			int x = tree[pos].size % 4 ;
			tree[pos].sum[x]+=cnt*k;
			tree[pos].size++;
		} else {
			int x = (tree[pos].size-1)%4;
			tree[pos].sum[x]+=cnt*k;
			tree[pos].size--;
		}
//		tree[pos].size+=cnt;
		return;
	}
	int mid=(l+r)>>1;
	if(k<=mid) update(lson,l,mid,k,cnt);
	else update(rson,mid+1,r,k,cnt);
	pushup(pos);
}
int ans,temp;
int get() {
	int res=tree[1<<1].sum[1]+tree[1<<1].sum[2];
	temp=ans;
	ans-=res;
	if(tree[2].size%2) { //奇数
		ans-=tree[3].sum[0]+tree[3].sum[2];
	} else {
		ans-=tree[3].sum[1]+tree[3].sum[3];
	}
	cout<<ans<<'\n';
	ans=temp;
}

void solve() {
	cin>>n>>m;
	for(int i=1; i<=n; i++) {
		cin>>a[i]>>b[i];
		update(1,-maxn,maxn,a[i]-b[i],1);
		ans+=a[i];
	}
	get();
	for(int i=1,x,y,z; i<=m; i++) {
		cin>>x>>y>>z;
		update(1,-maxn,maxn,a[x]-b[x],-1);
		ans-=a[x];	
		a[x]=y;	b[x]=z;
		update(1,-maxn,maxn,y-z,1);
		ans+=a[x];
		get();
	}
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t=1;
//	cin>>t;
	while(t--)solve();
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值