Codeforces 1137E - Train Car Selection

题目传送门
感觉官方题解已经讲得很好了……
有很多log的做法,但其实是可以线性的。(额,谁教教我log怎么做啊QAQ)

首先,我们注意到,每次加进去的一组0都是一起增加的,所以我们只需要关心这组0中的第一个(因为后面的肯定会永远大于等于第一个)。

然后我们又注意到,如果在最前面加入一些0,那么除了第一个以外的都永远不会是最大的,那么就相当于让所有 A i = 0 A_i=0 Ai=0

所以我们只需要关心2和3两个操作。

我们把所有 ( x , A x ) (x,A_x) (x,Ax)转化到二维平面上,然后维护一条向左下凸的折线,显然不在这个折线上的就永远不会是答案了。实际上答案就是折线上最后的点,就是右下角那个。

于是2操作就是加个点,然后和求凸包差不多,会删掉末尾的一些点。

3操作的话,我们发现经过操作之后所有线段的斜率都会变大,所以经过操作后凸折线的最后若干个点可能会“翘起来”,所以我们只需要从后往前,把那些“翘起来”的点删掉就行了。
在这里插入图片描述
有个小技巧。我们可以记录一下 b [ i ] , s [ i ] b[i],s[i] b[i],s[i]的前缀和,再结合点 ( x , A x ) (x,A_x) (x,Ax)是在第几个操作加进来的,就可以O(1)算出 A x A_x Ax,而不用在每次进行3操作后给每个凸折线上的点都加一波。

我计算几何弱渣,所以写得很丑QAQ

#include<bits/stdc++.h>
#define ll long long
#define x1 _x1
#define y1 _y1
#define x2 _x2
#define y2 _y2
#define fr(i,x,y) for(int i=(x);i<=(y);i++)
#define rf(i,x,y) for(int i=(x);i>=(y);i--)
#define frl(i,x,y) for(int i=(x);i<(y);i++)
using namespace std;
const int N=300003;
int n,m;
ll b[N],s[N];
struct data{
	int num,x;
}st[N];  //这是一个栈,维护凸折线上的点
int L;

inline int sign(ll &x){
	if (x>0) return 1;
	if (x<0) return -1;
	return 0;
}

inline int mul(ll x1,ll y1,ll x2,ll y2){  //这个是判断叉积是否>0的
	int w1=sign(x1)*sign(y2),w2=sign(y1)*sign(x2);
	if (w1!=w2) return w1>w2;
	if (w1==0) return 0;
	return (long double)x1*y2>(long double)y1*x2;
}

void read(int &x){ scanf("%d",&x); }
void read(ll &x){ scanf("%lld",&x); }

void chkmin(ll &x,ll y){ if (y<x) x=y; }

inline ll cal(data q,int w){ //计算A[q.x]
	return b[w]-b[q.num]+1ll*(q.x-1)*(s[w]-s[q.num]);
}

void AddPoint(int w,int x){
	if (cal(st[L],w)==0) return;
	ll y=cal((data){w,x},w);
	while(L>=2){
		if (mul(st[L].x-st[L-1].x,cal(st[L],w)-cal(st[L-1],w),x-st[L].x,y-cal(st[L],w))) break;
		L--;
	}
	st[++L]=(data){w,x};
}

void PopBack(int w){
	while(L>=2&&cal(st[L],w)>=cal(st[L-1],w)) L--;
}

int main(){
	read(n);read(m);
	int tp;ll x,y;
	st[L=1]=(data){0,1};
	fr(i,1,m){
		b[i]=b[i-1];s[i]=s[i-1];
		read(tp);read(x);
		if (tp==1){
			b[i]=s[i]=0;
			st[L=1]=(data){i,1};
			n+=x;
		}else if (tp==2){
			AddPoint(i,n+1);
			n+=x;
		}else{
			read(y);
			b[i]+=x;s[i]+=y;
			PopBack(i);
		}
		printf("%d %lld\n",st[L].x,cal(st[L],i));
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[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^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[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^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值