[ROI 2019 Day2]机器人高尔夫球赛

题目

传送门 to LOJ

思路

最少的分

O ( n m ) \mathcal O(nm) O(nm) 的动态规划走一波,大概能有 20 20 20 分。

更多的分

如果 n ≤ 5 n\le 5 n5 ,并且一个点的五列以内没有球洞,答案就是零。因为先后手都有将球打出界的方法。

全部的分

考虑优化 d p dp dp 。不难发现,让得分最大和最小的情况是类似的(将球洞的权值取相反数即可),这里只讨论让权值最小的情况。

沿用题面中 g g g 的定义。假设没有球洞的干扰,一定有 d p dp dp 转移式

g ( x , y ) = min ⁡ { max ⁡ [ g ( x + 2 , y ) , g ( x + 1 , y + 1 ) ] , max ⁡ [ g ( x + 1 , y + 1 ) , g ( x , y + 2 ) ] } g(x,y)=\min\{\max[g(x+2,y),g(x+1,y+1)],\max[g(x+1,y+1),g(x,y+2)]\} g(x,y)=min{max[g(x+2,y),g(x+1,y+1)],max[g(x+1,y+1),g(x,y+2)]}

其实就是把后手决策的这一步给压缩了

然后稍微化简一下得到

g ( x , y ) = max ⁡ { g ( x + 1 , y + 1 ) , min ⁡ [ g ( x + 2 , y ) , g ( x , y + 2 ) ] } g(x,y)=\max\{g(x+1,y+1),\min[g(x+2,y),g(x,y+2)]\} g(x,y)=max{g(x+1,y+1),min[g(x+2,y),g(x,y+2)]}

这个式子有啥意思捏?分类讨论呗。

  • 如果 g ( x + 1 , y + 1 ) < min ⁡ [ g ( x + 2 , y ) , g ( x , y + 2 ) ] g(x+1,y+1)<\min[g(x+2,y),g(x,y+2)] g(x+1,y+1)<min[g(x+2,y),g(x,y+2)] ,那么 g ( x , y ) g(x,y) g(x,y) 将会变成 min ⁡ \min min
  • 否则, g ( x , y ) g(x,y) g(x,y) 保持为 g ( x + 1 , y + 1 ) g(x+1,y+1) g(x+1,y+1)

如果我们将一个斜行看成一个单位,不难发现,大概就是这个意思:

偷盗来的图片,这里就不复制了,浪费网络资源。

用同一种颜色表示对应位。也就是说,对于每一个 左下-右上 对角线,看做一个序列,同种颜色的格子处于对应的序列中的相同位置。此时,我们研究 d p dp dp 值的变化,如果原本是一个凹陷 a > b < c a>b<c a>b<c ,那么 b ′ b' b 将会变成两边较低的那一个,使得其不再为低谷;如果不是凹陷,则 b ′ = b b'=b b=b

于是我们放心地说,在没有球洞的干扰时, d p dp dp 值在推导一次后将会变得“稳定”。这里的稳定指的是不与球洞直接相邻。

有球洞呢?球洞导致一个单点的 d p dp dp 值发生改变,这一位和周围两个点对应的 左上-右下 对角线需要推两个,以后都靠“稳定性”来求。这个点的上方、左边的点的 d p dp dp 值也会发生变化,也需要推两个。

x x x 表示直接变化的点, y y y 表示附属变化的点(受球洞干扰), z z z 表示让 d p dp dp 值变的稳定的步骤,则大概是

( z 0 z z z z y z 0 z y x ) \begin{pmatrix} & & & z_0\\ & & z & z\\ & z & z & y\\ z_0 & z & y & x \end{pmatrix} z0zzzzyz0zyx

z 0 z_0 z0 在我们的理论中是可能被影响到的,但实际上不会。若要探究为何,还需回归博弈本身,不难发现,先手可以保证球始终在起点周围两对角线内,即

( x y z y x y z z y x y z z y x y z y x ) \begin{pmatrix} x & y & z\\ y & x & y & z\\ z & y & x & y & z\\ & z & y & x & y\\ & & z & y & x \end{pmatrix} xyzyxyzzyxyzzyxyzyx

加上也是可以的。

代码

/*Lucky_Glass*/
#include<map>
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define fir first
#define sec second
const int N=1e5+10,MOD=998244353;
typedef pair<int,int> pii;

inline int Add(const int &A,const int &B){return A+B>=MOD? A+B-MOD:A+B;}
inline int Mul(const int &A,const int &B){return 1ll*A*B%MOD;}

map<int,int> las;
map<pii,int> key;

int rol,col,n;
vector<pii> sp;
pii f[N<<4]; //min(first),max(second)

inline int Ri(){
    register int a=0,b=1,c=getchar();
    while(c<'0' || '9'<c) b=c=='-'? -1:b,c=getchar();
    while('0'<=c && c<='9') a=(a<<1)+(a<<3)+c-'0',c=getchar();
    return a*b;
}
inline bool cmp(pii A,pii B){return A.fir+A.sec==B.fir+B.sec? A.fir<B.fir:A.fir+A.sec>B.fir+B.sec;}
inline pii Calc(int x,int y){
    if(key.count(make_pair(x,y))) return make_pair(key[make_pair(x,y)],key[make_pair(x,y)]);
    pii key1(0,0),key2(0,0);
    if(las.count(x-y+1)) key1=f[las[x-y+1]];
    if(las.count(x-y-1)) key2=f[las[x-y-1]];
    return make_pair(min(key1.sec,key2.sec),max(key1.fir,key2.fir));
}
inline int Model(const int &x){return (x%MOD+MOD)%MOD;}
int main(){
    rol=Ri(),col=Ri(),n=Ri();
    for(int i=1;i<=n;i++){
        int x=Ri(),y=Ri(),val=Ri();
        key[make_pair(x,y)]=val;
        //枚举4*4的三角,标记这些位置要暴力计算
        for(int p=0;p<4&&x-p>0;p++)
            for(int q=0;q<4-p&&y-q>0;q++)
                sp.push_back(make_pair(x-p,y-q));
    }
    //把要暴力计算的位置排序、去重
    sort(sp.begin(),sp.end(),cmp);
    sp.erase(unique(sp.begin(),sp.end()),sp.end());
    long long ans=0;
    // 从右下处理到左上,每次考虑一条对角线
    for(int o=0,lo=sp.size();o<lo;o++){
        int tag=sp[o].fir+sp[o].sec,beg=o;
        while(o+1<lo && sp[o+1].fir+sp[o+1].sec==tag) o++;
        //找到在同一条左下-右上对角线上的暴力计算的点
        for(int i=beg;i<=o;i++){
            int x=sp[i].fir,y=sp[i].sec;
            //统计当前左上-右下对角线原来的DP值出现的次数并计算贡献
            if(las.count(x-y)) ans=Add(ans,Mul(Model(f[las[x-y]].fir),sp[las[x-y]].fir-x));
            // 右边乘的是出现的长度
            f[i]=Calc(x,y); // 计算某个点的 DP 值
            las[x-y]=i; //记录当前左上-右下对角线的 DP 值
        }
    }
    //还有一些 DP 值的贡献
    for(map<int,int>::iterator it=las.begin();it!=las.end();it++)
        ans=Add(ans,Mul(Model(f[it->sec].fir),min(sp[it->sec].fir,sp[it->sec].sec)));
    printf("%d\n",ans);
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值