BZOJ 2165 大楼
BZOJ 2165 大楼
题目连接:https://remmina.github.io/BZPRO/JudgeOnline/2165.html
很明显,答案是具有单调性的。如果乘坐次数少了肯定不行,多了肯定行。我们第一反应一定是二分。但是这道题并不是二分。因为我们如果二分答案的话,那为什么不直接一步一步做上去,每做一次就检查一下答案是否满足,这样甚至时间复杂度是优于二分的。那么我们就想暴力,问题就变成了怎么加速暴力。答案具有单调性,而且需要我们去加速。显然倍增的优化就呼之欲出了。确定了基本想法。下面的问题就是怎么去倍增。
由于我们可以到达 \(n\) 个房间,每做一次的决策也是多样化的。如果只是预处理 w[x]
数组表示从 1 开始走 \(2^x\) 步能到达的最高高度。我们发现,由于决策的多样化,我们并不好处理出这样的数组。注意到既然决策是多种多样的,我们何不将所有的决策都倍增出来?注意到 \(n\) 的值域并不大,只有 100。我们可以用矩阵来表示每一种决策所导致的结果。具体点说,我们让一个矩阵 v[i][j]
表示从 \(i\) 到 \(j\) 的最大权值。如果不能到达的话 v[i][j]=-1
。我们设 w[i]
为一个矩阵他表示的是最多走\(2^i\)步所能产生的矩阵。转移式为:
乘法是什么意思,这里的乘法并不是真正矩阵的乘法,而是一种类似于 floyd 的思想。我们定义矩阵的乘法如下。
上界问题,我们就一直做 \(w_i=w_{i-1}\times w_{i-1}\) 这个操作。直到 \(w_{i}\) 满足条件。(在本题中满足条件的矩形 \(v\) 有 \(max(v_{1,i})\ge m\;(1\le i\le n)\))然后我们运用 倍增LCA 的思想,从大到小枚举 \(i\)。如果目前的矩阵 \(tmp\times w_i\) 不满足条件,我们就让 \(tmp\) 乘上一个 \(w_i\)。然后 \(ans\) 加上一个 \(2^i\)。最后再让 \(ans+1\)。总的来说,时间复杂度为 \(O(n^3\times up)\)。\(up\) 表示上界,最大不会超过 \(\log_2(10^{18})\) 。详情见代码。
代码如下:
#include<bits/stdc++.h>
#define ll long long
#define R register
using namespace std;
const int MAXN = 105;
int n;
ll m;
struct matrix
{
ll v[MAXN][MAXN];
matrix()
{
memset(v,-1,sizeof v);
}
matrix operator *(const matrix &x)
{
matrix ans;
for(R int k=1;k<=n;++k) for(R int i=1;i<=n;++i) for(R int j=1;j<=n;++j)
if(v[i][k]!=-1&&x.v[k][j]!=-1)
ans.v[i][j]=max(v[i][k]+x.v[k][j],ans.v[i][j]);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
ans.v[i][j]=min(m,max(ans.v[i][j],max(v[i][j],x.v[i][j])));
return ans;
}
}w[61];
bool judge(matrix a)
{
for(R int i=1;i<=n;++i)
if(a.v[1][i]>=m) return true;
return false;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %lld",&n,&m);
for(R int i=1;i<=n;++i)
for(R int j=1;j<=n;++j)
{
scanf("%lld",&w[0].v[i][j]);
if(!w[0].v[i][j]) w[0].v[i][j]=-1;
}
ll up=1,ans=0;
for(int i=1;;++i,++up)
{
w[i]=w[i-1]*w[i-1];
if(judge(w[i])) break;
}
for(R int i=1;i<=up;++i)
w[i]=w[i-1]*w[i-1];
bool flag=0;matrix tmp;
for(int i=up;i>=0;--i)
{
matrix res;
if(flag) res=tmp*w[i];
else res=w[i];
if(!judge(res))
{
tmp=res;
flag=1;
ans+=(1ll<<(ll)(i));
}
}
printf("%lld\n",ans+1);
}
return 0;
}
文末提一嘴,校内OJ上卡常,所以这个代码跑了4400ms(4000ms为AC),只有80分,理论上是能过的,所以在OJ上开了O2。不开O2的代码在这份的基础上卡卡常就能过,我比较懒所以开了O2