[BZOJ 2165] 大楼 【DP + 倍增 + 二进制】

题目链接:BZOJ - 2165

题目分析:

  这道题我读了题之后就想不出来怎么做,题解也找不到,于是就请教了黄学长,黄学长立刻秒掉了这道题,然后我再看他的题解才写出来。。Orz

  使用 DP + 倍增 ,用状态 f[x][i][j] 表示从 i 出发,坐 x 次电梯到达 j ,最多能上升的层数。开始读入的就是 f[1][][] 数组。(注意:若开始时 i 不能走到 j , 则 f[1][i][j] = -INF)

  使用倍增,用 f[x][][] 求出 f[x << 1][][] , 一直求f[2^p][][], 直到出现求出的 f[][][] 数组第一行存在大于等于 m 的数值。

  用 f[a][][] 和 f[b][][] 求出 f[a+b][][] 的状态转移方程是类似 Floyd 的 : f[a+b][i][j] = max(f[a][i][k] + f[b][k][j])

  之后枚举每一个二进制位,拼凑答案。如果加入这个二进制位后仍不能达到 m ,就加入这一位。最后答案要加1。(类似倍增求LCA)

代码如下:

  

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

const int MaxN = 100 + 5;

typedef long long LL;

const LL INF = 1e18;

int T, n, Top;

LL m, Ans;

struct Matrix
{
	LL Num[MaxN][MaxN];
	void Clear(LL x) {
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= n; ++j) {
				Num[i][j] = x;
			}
		}
	}
} M[70 + 5], M0, Temp;

LL gmax(LL a, LL b) {
	return a > b ? a : b;
}

Matrix Mul(Matrix A, Matrix B) {
	Matrix ret;
	ret.Clear(-INF);
	for (int k = 1; k <= n; ++k) {
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= n; ++j) {
				ret.Num[i][j] = gmax(ret.Num[i][j], A.Num[i][k] + B.Num[k][j]);
				if (ret.Num[i][j] > m) ret.Num[i][j] = m;
			}
		}
	}
	return ret;
}

bool Check(Matrix A) {
	for (int i = 1; i <= n; ++i)
		if (A.Num[1][i] >= m) return true;
	return false;
}

int main()
{
	scanf("%d", &T);
	for (int Case = 1; Case <= T; ++Case) {
		scanf("%d%lld", &n, &m);
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= n; ++j) {
				scanf("%lld", &M[0].Num[i][j]);
				if (M[0].Num[i][j] == 0ll) M[0].Num[i][j] = -INF;
			}
		}
		Top = 0;
		while (true) {
			++Top;
			M[Top] = Mul(M[Top - 1], M[Top - 1]);
			if (Check(M[Top])) break;
		}
		Ans = 0ll;
		M0.Clear(0);
		for (int i = Top; i >= 0; --i) {
			Temp = Mul(M[i], M0);
			if (!Check(Temp)) {
				M0 = Temp;
				Ans += (1ll << i);
			}
		}
		printf("%lld\n", Ans + 1);
	}
	return 0;
}

  

  

时间: 2024-10-11 13:42:40

[BZOJ 2165] 大楼 【DP + 倍增 + 二进制】的相关文章

bzoj 2165: 大楼【Floyd+矩阵乘法+倍增+贪心】

1<<i的结果需要是long long的话i是long long是没用的--要写成1ll<<i--我别是个傻子吧 虽然写的是二进制贪心,但是我觉得二分可能更好写吧(但是会慢) 首先把矩阵乘法转换成Floyd的形式,注意是进行一次更新,也就是另开一个数组使得更新之后每个[i][j]都变成经过一或两段路,(i,j)之间的最长路 然后就可以倍增了,一直相乘就会变成经过一二四六八-段路,(i,j)之间的最长路,这里把倍增过程记下来 直到某次相乘之后符合要求(也就是[1][x]的最长路大于等

BZOJ 2165 大楼 倍增Floyd

题目大意:给定一张图,求从1开始到达m的权值至少需要遍历多少条边 n<=100,果断倍增Floyd f[temp][i][j]表示经过2^temp条边从i走到j的最大权值 更新时f[temp[i][j]=max{f[temp-1][i][k]+f[temp-1][k][j]} 然后用矩阵g[i][j]记录当前走的权值,初始主对角线为0,其余为-∞ 从大到小枚举temp,利用f[temp]和g得到矩阵h 如果h中1到某个点的权值超过了m就跳出 否则将当前temp计入ans 然后将g数组更新为h 最

BZOJ 2165 大楼

倍增floyd然后按位确定. 注意long long的时候要1LL<i!!!!! #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 105 #define inf 1e18 using namespace std; long long t,n,m,f[maxn][maxn][maxn],g[maxn][maxn],h[maxn][

BZOJ 1192 鬼谷子的钱袋 (二进制思想)

题解:鉴于二进制的思想来划分 #include <cstdio> int main(){ int n,d=0;scanf("%d",&n); while(1<<d<=n)d++; printf("%d\n",d); } BZOJ 1192 鬼谷子的钱袋 (二进制思想),布布扣,bubuko.com

BZOJ 3572: [Hnoi2014]世界树 [虚树 DP 倍增]

传送门 题意: 一棵树,多次询问,给出$m$个点,求有几个点到给定点最近 写了一晚上... 当然要建虚树了,但是怎么$DP$啊 大爷题解传送门 我们先求出到虚树上某个点最近的关键点 然后枚举所有的边$(f,x)$,讨论一下边上的点的子树应该靠谁更近 倍增求出分界点 注意有些没出现在虚树上的子树 注意讨论的时候只讨论链上的不包括端点,否则$f$的子树会被贡献多次 学到的一些$trick:$ 1.$pair$的妙用 2.不需要建出虚树只要求虚树的$dfs$序(拓扑序)和$fa$就可以$DP$了 注意

Codeforces 418d Big Problems for Organizers [树形dp][倍增lca]

题意: 给你一棵有n个节点的树,树的边权都是1. 有m次询问,每次询问输出树上所有节点离其较近结点距离的最大值. 思路: 1.首先是按照常规树形dp的思路维护一个子树节点中距离该点的最大值son_dis[i],维护非子树节点中距离该点的最大值fa_dis[i]; 2.对于每个节点维护它最大的三个儿子节点的son_dis; 3.维护up[i][j]和down[i][j]数组,这个类似倍增lca里边的fa[i][j],up[i][j]代表的含义是从第j个点向上到它的第2^i个父节点这条链上的点除了该

【题解】互不侵犯 SCOI 2005 BZOJ 1087 插头dp

以前没学插头dp的时候觉得这题贼难,根本不会做,学了才发现原来是一裸题. 用二进制表示以前的格子的状态,0表示没放国王,1表示放了国王. 假设当前位置为(x,y),需要记录的是(x-1,y-1)至(x,y-1)的信息,共n+1个点. 每个状态有两种决策,第一种是这个格子不放国王,直接转移. 第二种是这个格子放国王,需要满足几个条件才能进行这步转移,条件很显然,具体见代码和注释. 因为状态数很少,所以也用不到BFS转移状态和Hash的技巧,所以这题还是很清真的,细节不多,码量也很小. #inclu

数位dp(二进制01问题)

题意:给你一个区间,求区间有多少个满足条件的数.条件是:把该数转为二进制后,如果0的数量大于等于1的数量,则为满足条件的数量. 题解:数位dp[pos][sta]表示第pos位,pos前面位数的0数量-1数量为sta的,且满足条件的数的数量. 因为在dfs过程中sta可能为负数(数组下标不能为负数),但又并不能却定该数最终是否符合条件,所以sta初始值为32就避免了负数.因为前导零会影响结果,所以要分类讨论. //#include <bits/stdc++.h> #include <cs

bzoj 1597 斜率DP

1597: [Usaco2008 Mar]土地购买 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 5115  Solved: 1897[Submit][Status][Discuss] Description 农夫John准备扩大他的农场,他正在考虑N (1 <= N <= 50,000) 块长方形的土地. 每块土地的长宽满足(1 <= 宽 <= 1,000,000; 1 <= 长 <= 1,000,000). 每块土地