C2 - Pokémon Army (hard version)(思维+差分/线段树+dp)详解

43 篇文章 0 订阅
16 篇文章 0 订阅

https://codeforces.com/contest/1420/problem/C2


这道题十分的锻炼思维,也让我知道了同样是差分,从前面减后面和从后面减前面是有不同的意义的。

还记得c1吗?我们是找波峰波谷的,实际上这个波峰波谷如果用差分的想法去找,可以做出c2的题。什么意思?

波峰波谷的差值一定是中间值之差的和,那么波峰波谷就是看成了能不能做差做下去。

比如1 2 5 4 3 6 7,从前往后差分是 -1,-3,1,2,-3,-1,7.会发现这样一个事情。把正数的差分全部加上去,就是最后求出的波峰波谷的最终值。

比如5 4 3找的是5 3(在上面的例子中)找出来的差值其实就是(5 - 4) + (4 - 3)

找到的那个最小值,一定波峰和波谷的值程单调性,那这两个值之间所有数的两两之差就是答案

如果画一个模拟的图5-4-3直线可以发现中间的点差不多没有影响,其实也就是这些值之间的两两之差。

那么发现了这个之后,后面的操作就是相当于把这个数组里的两个数字位置改变一下,重新进行这样类似的操作。

交换前考虑一下l的前后是不是正数,如果是就减掉,r的前后是不是正数,如果是就减掉。注意l==r-1的情况特判,防止多加多减。包含在里面一个情况就好了。

交换后看是否有相邻差为正的,正的就加上去。然后输出。swap就好了。

启发:找波峰波谷可以前往后差分去思考。

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=3e5+100;
typedef long long LL;
LL a[maxn];
void solve()
{
	LL n,q;cin>>n>>q;
	LL res=0;
	for(LL i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	for(LL i=1;i<=n;i++){
		if(a[i]-a[i+1]>0) res+=(a[i]-a[i+1]);
	}
	cout<<res<<endl;
	while(q--)
	{
		LL l,r;cin>>l>>r;
		if(a[l-1]-a[l]>0) res-=(a[l-1]-a[l]);
		if(a[r-1]-a[r]>0&&l!=r-1) res-=(a[r-1]-a[r]);
		if(a[l]-a[l+1]>0) res-=(a[l]-a[l+1]);
		if(a[r]-a[r+1]>0) res-=(a[r]-a[r+1]);
		
		swap(a[l],a[r]);
		if(a[l-1]-a[l]>0) res+=(a[l-1]-a[l]);
		if(a[r-1]-a[r]>0&&l!=r-1) res+=(a[r-1]-a[r]);
		if(a[l]-a[l+1]>0) res+=(a[l]-a[l+1]);
		if(a[r]-a[r+1]>0) res+=(a[r]-a[r+1]);
		cout<<res<<endl;
	}
}
int main(void)
{
  cin.tie(0);std::ios::sync_with_stdio(false);
  LL t;cin>>t;
  while(t--)
  {
  	memset(a,0,sizeof(a)); 
  	solve();
  }
return 0; 
}
 

最后补充一下线段树的dp。这个我是看别人提交的。但是我看不懂这个转移。

这个常数挺大的//写得丑了会TLE。

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
#define INF 1e18;
using namespace std;
const int maxn=3e5+100;
typedef long long LL;
int n,q;
int a[maxn];
struct Node{
	LL v[2][2];
	Node(){
		v[0][0]=v[1][1]=v[0][1]=v[1][0]=-INF;
	}
	Node(LL x){
		v[0][0]=x;
		v[1][1]=-x;
		v[0][1]=v[1][0]=-INF;
	}
	Node operator+(Node &p){
		Node ret;
		for(int i=0;i<2;i++){
			for(int j=0;j<2;j++)
			{
				ret.v[i][j]=max({v[i][j],p.v[i][j],v[i][0]+p.v[1][j],v[i][1]+p.v[0][j]});
			}
		}
		return ret;
	}
}tree[maxn*4];
void update(int p,int l,int r,int i,int x)
{
	if(l==r){
		tree[p]=Node(x);return;
	}
	int mid=(l+r)>>1;
	if(i<=mid) update(p*2,l,mid,i,x);
	if(i>mid) update(p*2+1,mid+1,r,i,x);
	tree[p]=tree[p*2]+tree[p*2+1];
}
int main(void)
{
  //cin.tie(0);std::ios::sync_with_stdio(false);
  int t;scanf("%d",&t);
  while(t--)
  {
  	scanf("%d%d",&n,&q);
  //	for(LL i=0;i<=n+10;i++) a[i]=0,update(1,1,n,i,a[i]); 
	for(int i=1;i<=n;i++) cin>>a[i],update(1,1,n,i,a[i]);
  	printf("%lld\n",tree[1].v[0][0]);
  	while(q--)
  	{
  		int l,r;scanf("%d%d",&l,&r);
  		swap(a[l],a[r]);
  		update(1,1,n,l,a[l]);
  		update(1,1,n,r,a[r]);
  		printf("%lld\n",tree[1].v[0][0]);
  	}
  }
return 0;
}
 

 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Pokémon Go开源项目是指将该游戏的源代码公开发布,供开发者和游戏爱好者免费使用和修改。这样一来,任何人都可以查看、学习、改进和共享该项目的代码。 将Pokémon Go开源的好处之一是促进了开源社区的发展。开源项目通常会吸引大量的开发者参与其中,他们可以为项目提供新的功能、修复错误和改进性能等贡献,从而提升游戏的质量和稳定性。这种合作模式可以促进技术的快速进步和创新,为用户带来更好的游戏体验。 另外,开源项目可以促进知识共享和学习。任何人都可以自由地查看和学习Pokémon Go的源代码,了解其中的技术实现和设计思路。对于新手开发者来说,这是一个非常好的学习资源,他们可以从中获取经验和灵感,提升自己的编程能力。 除此之外,开源项目还有利于游戏的改进和适应,特别是在特定地区或用户群体需求的情况下。开源代码使得游戏的定制化更加容易,可以针对特定的需求进行修改和适配,比如在某个国家或地区添加特定的功能或元素,或者根据用户的反馈进行改进和优化,从而更好地满足用户的需求。 总的来说,Pokémon Go开源项目的推出将会带来多方面的好处,包括促进开源社区的壮大、知识共享和学习的机会,以及游戏改进和适应的灵活性等。这对于开发者和玩家来说都是一个积极的举措。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值