acm-(思维,构造,转化,序列)Codeforces Round #672 (Div. 2) C2. Pokémon Army (hard version)

12 篇文章 0 订阅
10 篇文章 0 订阅

题面
传送门

最开始的想法很简单,观察这个正负交替的式子可以发现可以这样配对 a b 1 + ( a b 3 − a b 2 ) + ( a b 5 − a b 4 ) + . . . + ( a b k − a b k − 1 ) \mathbf{a_{b_1}+(a_{b_3}-a_{b_2})+(a_{b_5}-a_{b_4})+...+(a_{b_{k}}-a_{b_{k-1}})} ab1+(ab3ab2)+(ab5ab4)+...+(abkabk1)。显然括号中的值是大于等于0的。如果把数组中每个元素用点表示 ( i , a [ i ] ) \mathbf{(i,a[i])} (i,a[i]),那么一定会呈现出如下图1的形式:
图1
观察这个图,容易发现 a b 1 = ① , a b 2 = ② . . . \mathbf{a_{b_1}=①,a_{b_2}=②...} ab1=,ab2=...,这些元素都具有一个共性,那就是处于一个转折点处。但这个想法很初步,并且不太容易解决动态修改的问题。
尝试着进一步转化,我们发现在枚举i的过程中,其实只要满足 a [ i ] − a [ i − 1 ] > = 0 \mathbf{a[i]-a[i-1]>=0} a[i]a[i1]>=0我们就加到ans里去即可。对于 a 1 \mathbf{a_1} a1而言,我们在它前面添加一个元素 a 0 = 0 \mathbf{a_0=0} a0=0即可。如果仔细想一下就会发现这样做是等价于上图的计算方式的。还可以把这个方法写成一个数学表达式,即 a n s = ∑ i = 1 n m a x { 0 , a [ i ] − a [ i − 1 ] } \mathbf{ans=\sum_{i=1}^{n}max\{0,a[i]-a[i-1]\}} ans=i=1nmax{0,a[i]a[i1]}那么每个元素的贡献就非常单纯了,也就是 m a x { 0 , a [ i ] − a [ i ] − a [ i − 1 ] } \mathbf{max\{0,a[i]-a[i]-a[i-1]\}} max{0,a[i]a[i]a[i1]},如果修改的话就可以直接减去该元素原来的贡献,然后加上该元素更新后的贡献。注意对于 r − l ≤ 1 \mathbf{r-l\le 1} rl1的情况需要特判一下。

int n,a[maxn];
void add(int x,ll &ans,bool fg){//加上一个元素的贡献 
	if(!fg)ans+=max(0,a[x]-a[x-1]);
	if(x!=n)ans+=max(0,a[x+1]-a[x]);
}
void del(int x,ll &ans,bool fg){//减去一个元素的贡献 
	if(!fg)ans-=max(0,a[x]-a[x-1]);
	if(x!=n)ans-=max(0,a[x+1]-a[x]);
}
int main(){
	int t;
	rd(&t);
	while(t--){
		int q;
		rd(&n,&q);
		ll ans=0;
		FOR(i,1,n+1){
			rd(&a[i]);
			ans+=max(0,a[i]-a[i-1]);
		}
		wrn(ans);
		while(q--){
			int l,r;
			rd(&l,&r);
			if(l==r){//特判 
				wrn(ans);
				continue;
			}
			if(l+1==r){//特判 
				del(l,ans,false);
				del(r,ans,true); 
				swap(a[l],a[r]);
				add(l,ans,false);
				add(r,ans,true); 
			}else{
				del(l,ans,false);//减去旧的l对ans的贡献 
				del(r,ans,false);//减去旧的r对ans的贡献 
				swap(a[l],a[r]);
				add(l,ans,false);//加上新的l对ans的贡献 
				add(r,ans,false);//加上新的r对ans的贡献 
			}
			wrn(ans);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值