6.20训练日记

6.21的评论,6.20在搞学校工作,唉,训练还是得抓紧的

G. Count the Trains链接

思考了很久,但还是想不到怎么维护一些数据来达到每次更新的时候怎么快速求答案.一直在思考修改 i i i的位置,对原数组 a i − 1 , a i + 1 a_{i-1},a_{i+1} ai1,ai+1和根据题目定义的数组 b i + 1 b_{i+1} bi+1的影响,可以发现,修改 i i i并不会影响 i i i位置之前的答案贡献,但对 b i + 1 b_{i+1} bi+1后面有影响.如果 b i + 1 b_{i+1} bi+1的值没有变化,那答案只会在 a i − 1 a_{i-1} ai1那里有贡献,如果有变化,那么肯定是变得更小,那么就会使得后边的一些元素 b j > b i + 1 b_j>b_{i+1} bj>bi+1通通变成 b i + 1 b_{i+1} bi+1。这部分答案的贡献就是这些 b j b_j bj的数目。
那么问题来了,咋快速求呢,不会。
搬运标程了:用 b 数 组 代 表 映 射 完 毕 后 的 a 数 组 b数组代表映射完毕后的a数组 ba开一个set,保存那些连续段的起始坐标,也就是保存坐标 i i i,如果有 b i < b i − 1 b_i<b_{i-1} bi<bi1.那这个set的大小就是答案.
一开始的时候,不考虑修改的情况,当 a j > a m x , m x 是 集 合 S 里 面 的 最 大 值 , 代 表 上 一 个 答 案 的 下 标 a_j>a_{mx},mx是集合S里面的最大值,代表上一个答案的下标 aj>amx,mxS,,那么就会形成一个新的断点,需要我们把 j j j插入里面去.
当修改 a i a_i ai的时候,需要把所有 j > i 且 a j > a i j>i且a_j>a_i j>iaj>ai j j j从集合中全部删去.
在集合S里面, 下 标 j 越 大 , 原 数 组 对 应 的 a j 也 就 越 小 下标j越大,原数组对应的a_j也就越小 j,aj.然后修改后的 a j 与 下 标 j a_j与下标j ajj考虑是否能插入回原数组,
条件1: j j j下标原本就在这个集合里面了.
条件2: j j j下标不在,但修改后的 a j a_j aj比集合的前一个元素要小,
满足这两个条件之一就可以了.所以可以直接二分找到自己,比较二分指针前一个的值就能判断当前的值是否需要插入.
对于删除的操作,就是删掉所有 k > j 且 a k > a j k>j且a_k>a_j k>jak>aj
代码技巧:用rbegin代表迭代器最后一个元素,用prev(it)函数,找迭代器it的上一个.

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
const int INF = 1e9+7;
typedef long long ll;
typedef pair<int,int> pii;
#define all(a) (a).begin(), (a).end()
int main(){
    ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int T;cin>>T;
	while(T--){
		int n,m;cin>>n>>m;
		vector<int> a(n+1,0);
		for(int i=1;i<=n;i++) cin>>a[i];
		set<int> S;
		for(int i=1;i<=n;i++){
			if(S.empty()||a[*S.rbegin()]>a[i]){
				S.insert(i);
			}
		}
		while(m--){
			int j,d;cin>>j>>d;
			a[j]-=d;
			auto it = S.upper_bound(j);
			if(it!=S.begin()){
				it = prev(it);
				if(*it==j||a[*it]>a[j]) S.insert(j); 
			}
			else S.insert(j);
			while(true){
				it = S.upper_bound(j);
				if(it!=S.end()&&a[*it]>=a[j]) S.erase(it);
				else {
					break;
				} 
			}
			cout<<S.size()<<" ";
		}
		cout<<"\n";
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

minato_yukina

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

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

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

打赏作者

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

抵扣说明:

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

余额充值