首页 > 其他分享 >ABC365F Takahashi on Grid 题解

ABC365F Takahashi on Grid 题解

时间:2024-08-07 23:08:45浏览次数:8  
标签:sx return tx int 题解 单元格 Grid ABC365F now


## 题目大意

有一个网格图,对于 $i=1,2,\dots n$,第 $i$ 列的 $[L_i,U_i]$ 上的单元格是可到达的,形如下面这张图。

![](https://img.atcoder.jp/abc365/4d07a40c98eda33ee86b773e564681c7.png)

图中对于 $i=1,2,\dots7$,$[L_i,U_i]$ 分别为:

```
1 5
3 3
1 3
1 1
1 4
2 4
3 5
```

现有 $q$ 次询问,每次询问给定起点 $(sx,sy)$ 和终点 $(tx,ty)$。从起点出发,规定每次只能走到相邻的空单元格上,问最少走多少步能走到终点。

## Solve

首先考虑暴力怎么计算步数。假定起点在左,终点在右。贪心地,如果我当前单元格右侧是空的,直接向右走,否则走到右侧离他最近的空单元格。如此走到终点所在列之后再加上到终点的距离即可。$O(n)$ 暴力模拟之。

怎么优化?感觉要上一些数据结构,线段树不会,莫队感觉麻烦了,所以考虑分块。

对于一个起点,我一定是先一直水平向右走知道右侧没有空单元格,然后走到右侧空单元格的最上方或者最下方。所以对于每个块,我们维护如下信息:

- $sum_{i,0/1}$ 表示从第 $i$ 列空单元格的最下/上方开始,走到第 $i$ 列所在块的最右端所需最小步数。对于每一列都暴力模拟跑一遍即可,预处理总复杂度 $O(n\sqrt n)$。
- $p_{i,0/1}$ 表示从第 $i$ 列空单元格的最下/上方开始,走到第 $i$ 列所在块的最右端步数最小时,停在了第几行,处理 $sum$ 顺便记录下来即可。
- $mn_i$ 表示从第 $i$ 列所在块左端点到第 $i$ 列中,最上方的空单元格横坐标,即 $U$ 的最小值;$mx_i$ 表示从第 $i$ 列所在块左端点到第 $i$ 列中,最下方的空单元格横坐标,即 $L$ 的最大值。维护这些是为了方便求出一个单元格水平向右最多走到哪。

接下来考虑如何在询问时将相邻的块的信息拼接起来。

比如我们现在走到了第 $x$ 的块的右端点,横坐标为 $now$,我们需要找到第 $x+1$ 个块里,最左侧的第 $now$ 行不是空单元格的列。我们二分查找 $mn_{[l_{x+1},r_{x+1}]}$ 里第一个小于 $now$ 的位置 $p1$ 和 $mx_{[l_{x+1},r_{x+1}]}$ 里第一个大于 $now$ 的位置 $p2$,取 $\min$ 即可。

对于代价,若 $p1<p2$,令总代价加上 $sum_{p1,1}$,否则令总代价加上 $sum_{p2,0}$。即从第 $p1$ / $p2$ 列的最上/下方开始走到第 $x+1$ 块的最右端的代价。然后让 $now=p_{p1,1}$ / $p_{p2,0}$,继续下一块的拼接。

将整块拼接完后再特别处理一下终点所在散块的拼接,二分出第一个无法水平向右走到的位置,从那个位置开始暴力跑一遍代价即可。

询问总复杂度 $O(q\sqrt n\log_2n)$。显然可以调块长把 $\log$ 写到根号里,但本题不卡常。

## Code

```c++
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
	short f=1;
	int x=0;
	char c=getchar();
	while(c<'0'||c>'9')	{if(c=='-')	f=-1;c=getchar();}
	while(c>='0'&&c<='9')	x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
typedef long long ll;
const int N=2e5+10,M=500;
int n,q,a[N],b[N],sx,sy,tx,ty;
int m,len,l[M],r[M],p[N][2],pos[N],mn[N],mx[N];
ll sum[N][2];
inline ll calc(int x,int now,int y,int &lst)//从 (x,now) 走到第 y 列的最小代价,最终横坐标为 lst
{
	ll res=0;
	for(int i=x;i<y;i=-~i)
	{
		if(now>b[i+1])	res+=now-b[i+1],now=b[i+1];
		if(now<a[i+1])	res+=a[i+1]-now,now=a[i+1];
	}
	return lst=now,res;
}
inline void pre(int u)//预处理第 u 块的信息
{
	for(int i=r[u];i>=l[u];i--)
		sum[i][0]=calc(i,a[i],r[u],p[i][0]),
		sum[i][1]=calc(i,b[i],r[u],p[i][1]);
	mn[l[u]]=b[l[u]];mx[l[u]]=a[l[u]];
	for(int i=l[u]+1;i<=r[u];i=-~i)
		mn[i]=min(b[i],mn[i-1]),mx[i]=max(a[i],mx[i-1]);
}
inline int lower(int l,int r,int x)//mn[l~r] 中第一个比 x 小的位置
{
	if(mn[r]>=x)	return r+1;
	while(l<r)
	{
		int mid=l+r>>1;
		if(mn[mid]<x)	r=mid;
		else	l=-~mid;
	}
	return l;
}
inline int upper(int l,int r,int x)//mx[l~r] 中第一个比 x 大的位置
{
	if(mx[r]<=x)	return r+1;
	while(l<r)
	{
		int mid=l+r>>1;
		if(mx[mid]>x)	r=mid;
		else	l=-~mid;
	}
	return l;
}
inline ll query()
{
	int x=pos[sx],y=pos[tx],now;
	if(x==y)	return calc(sx,sy,tx,now)+abs(now-ty);
	ll res=calc(sx,sy,r[x],now);//对于两边的散块,暴力跑代价
	for(int i=x+1,p1,p2;i<y;i=-~i)
	{
		p1=upper(l[i],r[i],now),p2=lower(l[i],r[i],now);
		if(min(p1,p2)>r[i])	continue;//如果能水平向右走到右端点
		if(p1<p2)	res+=sum[p1][0]+a[p1]-now,now=p[p1][0];
		else	res+=sum[p2][1]+now-b[p2],now=p[p2][1];
	}
	int p1=upper(l[y],tx,now),p2=lower(l[y],tx,now);//拼接终点所在散块
	if(min(p1,p2)<=tx)
	{
		if(p1<p2)	res+=a[p1]-now+calc(p1,a[p1],tx,now);
		else	res+=now-b[p2]+calc(p2,b[p2],tx,now);
	}
	return res+abs(now-ty);//加上同一列里走到终点横坐标的代价
}
signed main()
{
	n=read();len=sqrt(n*1.0);m=n/len;
	for(int i=1;i<=m;i=-~i)
		l[i]=-~r[i-1],r[i]=r[i-1]+len;
	if(n%len)	m=-~m,l[m]=r[m-1]+1,r[m]=n;
	for(int j=1;j<=m;j=-~j)
		for(int i=l[j];i<=r[j];i=-~i)
			a[i]=read(),b[i]=read(),pos[i]=j;
	for(int i=1;i<=m;i=-~i)	pre(i);
	q=read();
	while(q--)
	{
		sx=read();sy=read();tx=read();ty=read();
		if(sx>tx)	swap(sx,tx),swap(sy,ty);//默认从左往右走
		printf("%lld\n",query()+tx-sx);//别忘了加上横向代价
	}
	return 0;
}
```

标签:sx,return,tx,int,题解,单元格,Grid,ABC365F,now
From: https://www.cnblogs.com/sorato/p/18343401

相关文章

  • excel总结遗留问题解决
    excel遗留问题解决powerquery这是powerbi中的一部分,excel2016以后集成了powerquery,用于做数据清洗。一般过程是数据导入powerquery,经过powerquery清洗,然后上载到excel的表,数据透视表等以共使用。插入之定义列,然后使用公式生成新的列数据?函数配合条件选择使用......
  • loj6669 Nauuo and Binary Tree 题解
    https://loj.ac/p/6669赛时做法先\(n-1\)次问出深度逐层考虑。slv(vector<int>a,vector<int>b)表示在点集\(a\)中寻找\(b\)中点的父亲,询问\(a[0]\)和\(b\)中所有点的距离分治下去复杂度不会算,印象中过了树剖oiwiki二叉树:最多只有一个轻儿子类似「即时战略」......
  • 洛谷P1064 金明的预算方案——题解
    洛谷P1064题解传送锚点摸鱼环节[NOIP2006提高组]金明的预算方案题目描述金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过\(n\)元钱就行”。今天......
  • 历年CSP-J初赛真题解析 | 2013年CSP-J初赛阅读程序(23-26)
    学习C++从娃娃抓起!记录下CSP-J备考学习过程中的题目,记录每一个瞬间。附上汇总贴:历年CSP-J初赛真题解析|汇总_热爱编程的通信人的博客-CSDN博客#include<iostream>usingnamespacestd;intmain(){inta,b;cin>>a>>b;cout<<a<<"+"<<b<<......
  • 洛谷P3842 线段——题解
    洛谷P3842题解传送锚点摸鱼环节[TJOI2007]线段题目描述在一个\(n\timesn\)的平面上,在每一行中有一条线段,第\(i\)行的线段的左端点是\((i,L_{i})\),右端点是\((i,R_{i})\)。你从\((1,1)\)点出发,要求沿途走过所有的线段,最终到达\((n,n)\)点,且所走的路程长度要尽......
  • Gym102788,B - Rectangles题解
    水水水~题目链接戳我分析首先根据题目条件可得式子=>\((x-2)(y-2)=n(2x+2y-4)\)化简式子可得\[\begin{align}(x-2)(y-2)=&n(2x+2y-4)\\xy-2x-2y+4=&2nx+2ny-4n\\x(y-2n-2)=&2ny-4n-4+2y\\x=&\frac{2ny-4n-4......
  • 题解:Codeforces Round 964 (Div. 4) D
    D.Slavic'sExamtimelimitpertest:2secondsmemorylimitpertest:256megabytesinput:standardinputoutput:standardoutputSlavichasaverytoughexamandneedsyourhelpinordertopassit.Hereisthequestionheisstrugglingwith:Ther......
  • 数列 题解
    题目id:1753题目描述给你一个长度为\(N\)的正整数序列,如果一个连续的子序列,子序列的和能够被K整除,那么就视此子序列合法,求原序列包括多少个合法的连续子序列?对于一个长度为\(8\)的序列,\(K=4\)的情况:\(2,1,2,1,1,2,1,2\)。它的答案为\(6\),子序列是位置\(1->\)位置\(8\),\(2->4\)......
  • 题解:Codeforces Round 964 (Div. 4) C
    C.Showeringtimelimitpertest:2secondsmemorylimitpertest:256megabytesinput:standardinputoutput:standardoutputAsacomputersciencestudent,Alexfacesahardchallenge —showering.Hetriestoshowerdaily,butdespitehisbestefforts......
  • CF1689C题解
    CF1689CInfectedTree题解怎么只有我这个蒟蒻不会DP啊。回归正题……简化题意给定一棵以$1$号节点为根的二叉树,总节点个数为$n$。现在$1$号节点感染了病毒,你在这一回合里可以使病毒所在节点的一个儿子不被感染,而病毒会感染一个不受保护的儿子。询问最多可以有多少不......