D. 小srf的游戏(倒序思维+博弈+dp单调队列/线段树优化)

https://blog.csdn.net/zstuyyyyccccbbbb/article/details/112912657(先附上一个比较类似的题)


思路:观察样例,srf先手和qtc先手的答案就是取了个负号。所以就考虑srf先手的时候获得的得分-qtc得分的最大值。

为什么取负号呢?举个例子:

2 2

1 2

srf先手,必然是取2最优,这样qtc就没得取了。于是第一个答案为2

qtc先手,题目的意思实际上是想让你先考虑qtc-srf获得的最大得分 ,同样的,此时把qtc看成srf即可,取2是最优的。得分为2.最后题目说不管如果,输出srf-qtc的分,所以取负数。

因此到这里我们就单纯考虑srf先手的情况。

先正着想,srf取了i这个位置之后,qtc可以取i+1~i+m的一个数,但是正着扫过去的话会影响srf的后续状态。

到这里,这个题目就启发我们:如果正着状态是冲撞的,那么倒着来。而且极可能是一个类似dp的转移。

扫描二维码关注公众号,回复: 12165064 查看本文章

这里可以看一下昨天一场的烽火台题目,仿照设计。从后往前,dp[i]表示srf取了i这个数,此时[i~n]获得的srf先手-qtc得分的最大值。

考虑转移:dp[i]=a[i]-max(dp[i+1~i+m]);

这个转移各有各的方式,群友有的方程设计也不一样,转移也不同,然后我晕着迷糊了一晚上,早上按照自己的想法把这个题做了。如果不懂可以参考其他群友的博客。

(https://blog.csdn.net/weixin_45948940/article/details/112934074?utm_source=app&app_version=4.5.0

https://blog.csdn.net/tlyzxc/article/details/112976251

这个转移是这样的。srf取了i,由于博弈,也就是对手是个阿尔法狗,他会去找一个剩下的他能获得的最大值来使得这个答案尽可能小。在[i+1~i+m]中,狗是先手,他取一个最大的状态,也就是之前已经更新过的srf先手的一个状态,挑一个最大来使得得分变小。(博弈嘛)

最后这里是一个需要找最值的dp。我单调队列不是很熟悉,就上了线段树。单调队列的优化可以参考上面群聚的)

(注释处是朴素方程)

#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=1e5+100;
typedef long long LL;
LL dp[maxn],a[maxn];
struct Tree{
    LL l,r,maxval,tag;///区间最大值
}tree[maxn*4];
void push_up(LL p)
{
    tree[p].maxval=max(tree[p*2].maxval,tree[p*2+1].maxval);
}
void addtag(LL p,LL d)
{
    tree[p].tag=d;
    tree[p].maxval=tree[p].tag;
}
void push_down(LL p)
{
    if(tree[p].tag!=-1){
        addtag(p*2,tree[p].tag);
        addtag(p*2+1,tree[p].tag);
        tree[p].tag=-1;
    }
}
void build(LL p,LL l,LL r)
{
    tree[p].l=l;tree[p].r=r;tree[p].maxval=0;
    tree[p].tag=-1;
    if(l==r) {tree[p].maxval=0;return;}
    LL mid=(l+r)>>1;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    push_up(p);
}
void modify(LL p,LL l,LL r,LL d)
{
    if(l<=tree[p].l&&r>=tree[p].r)///要修改的区间涵盖在区间内部
    {
            addtag(p,d);
            return;
    }
    push_down(p);
    LL mid=(tree[p].l+tree[p].r)>>1;
    if(l<=mid) modify(p*2,l,r,d);
    if(r>mid) modify(p*2+1,l,r,d);
    push_up(p);
}
LL query(LL p,LL l,LL r)
{
    if(l<=tree[p].l&&r>=tree[p].r)
    {
        return tree[p].maxval;
    }
    LL ans=-1e18;
    push_down(p);
    LL mid=(tree[p].l+tree[p].r)>>1;
    if(l<=mid) ans=max(ans,query(p*2,l,r));
    if(r>mid) ans=max(ans,query(p*2+1,l,r));
    return ans;
}
int main(void)
{
  cin.tie(0);std::ios::sync_with_stdio(false);
  LL n,m;cin>>n>>m;
  build(1,1,n);
  for(LL i=1;i<=n;i++) cin>>a[i];
  for(LL i=1;i<=max(m,n)+10;i++) dp[i]=-1e18;
  dp[n]=a[n];  
  modify(1,n,n,a[n]);
  for(LL i=n-1;i>=1;i--){
    LL temp=-1e18;
    temp=max(temp,query(1,i+1,min(i+m,n)));
   // for(LL j=i+1;j<=min(i+m,n);j++){
   //     temp=max(dp[j],temp);
   // }
    dp[i]=a[i]-temp;
    modify(1,i,i,dp[i]);
  }
  LL ans=-0x3f3f3f3f;
  for(LL i=1;i<=m;i++){
    ans=max(ans,dp[i]);
  }
  cout<<ans<<endl<<-ans<<endl;
return 0;
}

猜你喜欢

转载自blog.csdn.net/zstuyyyyccccbbbb/article/details/112981622