Toyota Programming Contest 2024#4(AtCoder Beginner Contest 348)G. Max (Sum - Max) (决策单调性+主席树前k大)

题目

长为n(n<=2e5)的两个序列a和b,第i个数分别ai(-1e9<=ai<=1e9),bi(-2e14<=bi<=2e14)

对于k从1到n,分别解决如下问题:

从[1,n]中选k个不同的位置,构成集合S,要求最大化\sum_{i\epsilon S} a_{i}-max_{i\epsilon S} b_{i},输出这个最大值

思路来源

uoj群qls

G题:https://qoj.ac/problem/7523


题解

主要是需要注意到决策单调性,注意到这个之后就比较好做了

考虑k=x的时候,已经在i处取到了[1,i]的最优决策,

那么当i变为i+1的时候,如果无脑选第i+1个位置,就构成了一种k=x+1的决策

a[i+1]-a[rank[x](选的第x大的值)]-b[i+1]+b[i]>0的时候,能使k=x的决策更优

此时k=x的最优决策在i+1处取到,否则还是在第i处取到

反过来看,如果k=x+1的最优决策在j,

那么,k=x的最优决策要么在j(也就是通过满足上述加粗式,把之前最优决策里最小的a值扔掉)

要么在小于j的位置,也就是不满足加粗式,还是之前的决策方式

倘若k=x最优决策是在大于j的位置取到的,那么一定是将k=x在j处决策后的最优决策调的更优了,

把相同的调优手段应用给k=x+1,也能使k=x+1更优,与k=x+1的最优决策在j矛盾

综上,k=x决策点不大于k=x+1决策点

对于每个位置决策,主席树上维护一下当前的值有哪些,主要是需要查询topk大

分治一下,共查询O(nlogn)个位置的决策

代码

// Problem: G - Max (Sum - Max)
// Contest: AtCoder - Toyota Programming Contest 2024#4(AtCoder Beginner Contest 348)
// URL: https://atcoder.jp/contests/abc348/tasks/abc348_g
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=2e5+10,M=200*N;
const ll INF=1e18;
P d[N];
int n,a[N],m,x[N],tot;
int root[N],ls[M],rs[M],c;
ll b[N],sum[M],cnt[M],ans[N];
void upd(int l,int r,int &cur,int las,int pos,int v){
	cur=++c;
    ls[cur]=ls[las];rs[cur]=rs[las];
    sum[cur]=sum[las]+v;
    cnt[cur]=cnt[las]+1;
	if(l==r){
        return;
	}
	int mid=(l+r)/2;
	if(pos<=mid)upd(l,mid,ls[cur],ls[las],pos,v);
	else upd(mid+1,r,rs[cur],rs[las],pos,v);
}
ll ask(int cur,int l,int r,int k){
	//printf("l:%d r:%d k:%d cnt:%d\n",l,r,k,cnt[cur]);
	if(cnt[cur]<k)return -INF;
    if(!cur)return 0;
    if(l==r){
    	//printf("l:%d x:%d\n",l,x[l]);
    	return 1ll*k*x[l];
    }
    int mid=(l+r)/2,num=cnt[rs[cur]];
    ll res=0;
    if(num>=k)return ask(rs[cur],mid+1,r,k);
    return sum[rs[cur]]+ask(ls[cur],l,mid,k-num);
}
ll cal(int p,int k){
	ll w=ask(root[p],1,m,k)-d[p].fi;
	//printf("p:%d k:%d dp.fi:%lld w:%lld\n",p,k,d[p].fi,w);
	return w;
}
void dfs(int l,int r,int pl,int pr){
  	int mid=(l+r)/2,p=pl;
    ll res=cal(pl,mid);
    for(int j=pl+1;j<=pr;j++){
        ll tmp=cal(j,mid);
        if(tmp>res){
            res=tmp;
            p=j;
        }
    }
    ans[mid]=res;
    if(l<mid)dfs(l,mid-1,pl,p);
    if(mid<r)dfs(mid+1,r,p,pr);
}
int main(){
	sci(n);
	rep(i,1,n){
		scanf("%d%lld",&a[i],&b[i]);
		x[i]=a[i];
		d[i]=P(b[i],a[i]);
	}
	sort(x+1,x+n+1);
	m=unique(x+1,x+n+1)-(x+1);
	sort(d+1,d+n+1);
	rep(i,1,n){
		ll y=d[i].fi,z=d[i].se;
		int p=lower_bound(x+1,x+m+1,z)-x;	
		root[i]=root[i-1];
		//printf("i:%d p:%d z:%lld\n",i,p,z);
		upd(1,m,root[i],root[i],p,z);
	}
	dfs(1,n,1,n);
	rep(i,1,n){
		printf("%lld\n",ans[i]);
	}
	return 0;
}

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
回答: AtCoder Beginner Contest 314是一场编程竞赛。根据引用中的代码,这段代码是一个解决一个关于组合问题的程序。它通过深度优先搜索来确定每个人应该被安排在哪个组中。其中,n表示人的数量,T表示组的数量,m表示冲突的对数。程序会根据给定的冲突对来判断是否可以将一个人放入某个组中。如果所有的条件都满足,则输出方案数。根据引用和引用中的代码,我们可以使用不同的方法来解决这个问题。引用中的代码使用了set来维护每个人的出现次数,并按照出现次数之和的降序排列,然后按照顺序将数字放入构建的序列中。而引用中的代码使用了二维数组和vector来实现。不同的组会被存储在一个vector中,然后通过深度优先搜索来确定每个人应该被放置在哪个组中。这两种方法都可以解决问题,具体使用哪一种方法取决于实际需求和个人喜好。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [AtCoder Beginner Contest 310 D](https://blog.csdn.net/XihuDacuyu/article/details/131749177)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [【解题总结】Atcoder Beginner Contest 汇总](https://blog.csdn.net/zqy1018/article/details/108167389)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值