博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[JZOJ6278] 2019.8.5【NOIP提高组A】跳房子
阅读量:5292 次
发布时间:2019-06-14

本文共 2841 字,大约阅读时间需要 9 分钟。

题目大意

给你一个矩阵,从\((1,1)\)开始,每次往右上、右、右下三个格子中权值最大的那个跳。

第一行上面是第\(n\)行,第\(m\)列右边是第\(1\)列。反之同理。
有两个操作:跳\(K\)步和修改某行某列的权值。
\(n,m\leq 2000\)


思考历程

一开始觉得似乎可以倍增,但这个修改操作太烦人,想了很久感觉倍增不可做。

最终打暴力+判断循环节。然而爆\(10\)了。
后来发现少打了个\(+1\),加上之后,居然水了\(85\)分。


正解

\(jump_i\)表示\(i\)\(1\)列开始跳\(m\)步会到哪一行。

有了这个东西,询问就很好做了。先跳到\(1\)列,然后每次\(m\)\(m\)步地跳,判一下循环节。
重点是这个东西怎么维护。
按照题解做法,在某个点修改之后往前搞。由于改变方向的点都是在一个区间之内的,所以维护左端点和右端点,一直做到\(1\)列即可。
然而……
无数人有实践表明,这样打不出啊!!!
细节太多了……

于是有个造福人类的线段树做法。

我们可以计算出\(i\)列到\(i+1\)列的映射,用个长度为\(n\)的数组存下来。
然后利用线段树合并,处理出\(1\)列到\(n+1\)列的映射,也就是\(jump\)数组。
查询的时候一模一样。至于修改,直接单点修改,单次修改复杂度\(O(n\lg m)\)
也就比题解做法多了一个\(lg\)而已,但代码可要方便很多。


代码

(线段树做法)

using namespace std;#include 
#include
#include
#define N 2010inline int input(){ char ch=getchar(); while (ch<'0' || '9'
a[x][y]) x=ux; if (a[dx][y]>a[x][y]) x=dx; return x;}inline void get_next(int &x,int &y){ x=nxt(x,y); y=ri(y);}int jump[N<<4][N];int vis[N],BZ,tim[N];void build(int k,int l,int r){ if (l==r){ for (int i=1;i<=n;++i) jump[k][i]=nxt(i,l); return; } int mid=l+r>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); for (int i=1;i<=n;++i) jump[k][i]=jump[k<<1|1][jump[k<<1][i]];}void change(int k,int l,int r,int y){ if (l==r){ for (int i=1;i<=n;++i) jump[k][i]=nxt(i,y); return; } int mid=l+r>>1; if (y<=mid) change(k<<1,l,mid,y); else change(k<<1|1,mid+1,r,y); for (int i=1;i<=n;++i) jump[k][i]=jump[k<<1|1][jump[k<<1][i]];}int main(){ freopen("jump.in","r",stdin); freopen("jump.out","w",stdout); n=input(),m=input(); for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) a[i][j]=input(); build(1,1,m); int Q; scanf("%d",&Q); char op[7]; while (Q--){ scanf("%s",op); if (*op=='m'){ int k=input(),i; for (;k && nowy!=1;--k) get_next(nowx,nowy); if (k==0){ printf("%d %d\n",nowx,nowy); continue; } vis[nowx]=++BZ; tim[nowx]=i=0; while (k>=m){ k-=m; ++i; nowx=jump[1][nowx]; if (vis[nowx]!=BZ){ vis[nowx]=BZ; tim[nowx]=i; continue; } k%=m*(i-tim[nowx]); break; } for (;k>=m;k-=m) nowx=jump[1][nowx]; for (;k;--k) get_next(nowx,nowy); printf("%d %d\n",nowx,nowy); } else{ int x=input(),y=input(),c=input(); a[x][y]=c; change(1,1,m,le(y)); } } return 0;}

总结

好多时候都可以用到线段树呢……

转载于:https://www.cnblogs.com/jz-597/p/11329455.html

你可能感兴趣的文章
SAP HANA 三大特点
查看>>
C#预处理器命令
查看>>
苹果手表:大方向和谷歌一样,硬件分道扬镳
查看>>
ccf 出现次数最多的数
查看>>
单例模式
查看>>
Competing Consumers Pattern (竞争消费者模式)
查看>>
HDUOJ ------1398
查看>>
cf--------(div1)1A. Theatre Square
查看>>
Android面试收集录15 Android Bitmap压缩策略
查看>>
Tomcat 报错的解决方法:The APR based Apache Tomcat Native library which allows optimal
查看>>
最长公共子串问题(LCS)
查看>>
TortoiseSVN is locked in another working copy
查看>>
PHP魔术方法之__call与__callStatic方法
查看>>
ubuntu 安装后的配置
查看>>
Html学习_简易个人网页制作
查看>>
angular中ng-bind指令小案例
查看>>
jqery总结
查看>>
Lodop获取客户端主网卡ip地址是0.0.0.0
查看>>
VSCODE更改文件时,提示:EACCES: permission denied的解决办法(mac电脑系统)
查看>>
web前端之路,js的一些好书(摘自聂微东 )
查看>>