YbtOJ 「数据结构」 第6章 倍增问题

A. 【例题1】查找编号

[题目描述]
输入\(n\)个不超过\(10^9\)的单调不减的(就是后面的数字不小于前面的数字)非负整数\(a_1,a_2\dots a_n\),第\(i\)个数的编号为\(i\),然后进行\(m\)次询问。对于每次询问,给出一个整数\(q\),要求输出这个数字在序列中的编号,如果没有找到的话输出 -1。当多个数值相同时,这个数的编号取最靠前的那个。

[输入格式]
第一行\(2\)个整数\(n\)\(m\),表示数字个数和询问次数。

第二行\(n\)个整数,表示这些待查询的数字。

第三行\(m\)个整数,表示询问这些数字的编号,从 开始编号。

[输出格式]

一行\(m\)个整数表示每个询问对应的答案。

[算法分析]

倍增板子 确定第一个小于目标值的位置

[代码实现]

#include <bits/stdc++.h>
using namespace std;
#define inl inline 
const int N = 1e6 + 5;

inl int read ()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int m , n , a[N] , x;
int get ( int x )
{
	int ans = 0;
	for ( int i = 20 ; i >= 0 ; i -- )
		if ( ans + (1<<i) <= n )
			if ( a[ans+(1<<i)] < x )//右面指针小于目标值 
				ans += (1<<i);
	if ( a[ans+1] == x ) return ans + 1;
	else return -1;
}
int main()
{
	scanf ( "%d%d" , &n , &m );
	for ( int i = 1 ; i <= n ; i ++ ) scanf ( "%d" , &a[i] );
	while ( m -- ) printf ( "%d " , get(read()));
	return 0;
}  

C. 1.跑路上班

[题目描述]

小 A 的工作不仅繁琐,更有苛刻的规定,要求小 A 每天早上在 \(6:00\) 之前到达公司,否则这个月工资清零。可是小 A 偏偏又有赖床的坏毛病。于是为了保住自己的工资,小 A 买了一个空间跑路器,每秒钟可以跑 \(2^k\) 千米(\(k\) 是任意自然数)。当然,这个机器是用 longint 存的,所以总跑路长度不能超过 maxlongint 千米。小 A 的家到公司的路可以看做一个有向图,小 A 家为点 \(1\),公司为点 \(n\),每条边长度均为一千米。小 A 想每天能醒地尽量晚,所以让你帮他算算,他最少需要几秒才能到公司。数据保证 \(1\)\(n\) 至少有一条路径。

[输入格式]

第一行两个整数 \(n,m\),表示点的个数和边的个数。

接下来 \(m\) 行每行两个数字 \(u,v\),表示一条 \(u\)\(v\) 的边。

[输出格式]

一行一个数字,表示到公司的最少秒数。

[算法分析]

处理所有可以一步到达的点 边权设置为1

最后跑一边floyd处理即可

[代码实现]

#include <bits/stdc++.h>
using namespace std;
#define inl inline 
#define int long long
const int N = 1e2 + 5;
const int inf = 0x3f3f3f3f;

inl int read ()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int m , n , a[N] , k , dis[N][N][N] , f[N][N];

int head[N] , cnt;
struct node { int to , nxt , w; } e[N];
void add ( int u , int v , int w ) { e[++cnt] = { v , head[u] , w }; head[u] = cnt; }

signed main()
{
	n = read() , m = read();
	memset ( f , inf , sizeof f );	
	for ( int i = 1 , u , v ; i <= m ; i ++ )
		u = read() , v = read() , dis[u][v][0] = f[u][v] = 1;
	for ( int i = 1 ; i <= 64 ; i ++ )
		for ( int k = 1 ; k <= n ; k ++ )
			for ( int u = 1 ; u <= n ; u ++ )
				for ( int v = 1 ; v <= n ; v ++ )
					dis[u][v][i] = max ( dis[u][v][i] , dis[u][k][i-1] & dis[k][v][i-1] );
	for ( int u = 1 ; u <= n ; u ++ )
		for ( int v = 1 ; v <= n ; v ++ )
			for ( int k = 1 ; k <= 64 ; k ++ )
				if ( dis[u][v][k] ) { f[u][v] = 1; break; }
	for ( int k = 1 ; k <= n ; k ++ )
		for ( int u = 1 ; u <= n ; u ++ )
			for ( int v = 1 ; v <= n ; v ++ )
				f[u][v] = min ( f[u][v] , f[u][k] + f[k][v] );
	printf ( "%lld" , f[1][n] );
	return 0;
}  

D. 2.图上查询

[题目描述]

有一个\(n\)个点\(n\)条边的有向图,第\(i\)条边从\(i-1\)指向\(f_i\),边权为\(w_i\)的边,对每个点求从它出发经过\(k\)条边,这\(k\)条边的权值和 \(s_i\)以及这\(k\)条边的权值最小值\(m_i\)

[输入格式]

第一行两个数\(n,k\)

第二行\(n\)个数\(f_i\)

第三行\(n\)个数\(w_i\)

[输出格式]

每行两个数\(s_i\),\(m_i\).

[代码实现]

节点从0开始标号
\(nxt[i][k]\)表示节点\(i\)\(2^j\)步到达的节点,\(mn[i][j]\)表示节点\(i\)\(2^j\)步之间路长度的最小值,\(sum[i][j]\)表示的路径长度和。

[代码实现]

#include <bits/stdc++.h>
using namespace std;
#define inl inline 

#define int long long 

const int N = 1e5 + 5;
const int inf = 0x3f3f3f3f3f3f3f3f;

inl int read ()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}
int m , n , fa[N][40] , minn[N][40] , sum[N][40];
signed main()
{
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) fa[i][0] = read() + 1;
	for ( int i = 1 ; i <= n ; i ++ ) sum[i][0] = minn[i][0] = read();
	for ( int j = 1 ; j <= 35 ; j ++ )
		for ( int i = 1 ; i <= n ; i ++ )
			fa[i][j] = fa[fa[i][j-1]][j-1];
	for ( int j = 1 ; j <= 35 ; j ++ )
		for ( int i = 1 ; i <= n ; i ++ )
			minn[i][j] = min ( minn[i][j-1] , minn[fa[i][j-1]][j-1] );
	for ( int j = 1 ; j <= 35 ; j ++ )
		for ( int i = 1 ; i <= n ; i ++ )
			sum[i][j] = sum[i][j-1] + sum[fa[i][j-1]][j-1];
	for ( int i = 1 ; i <= n ; i ++ )
	{
		int mn = inf , summ = 0 , now = i;
		for ( int j = 35 ; j >= 0 ; j -- )
		{
			if ( m & ( 1ll << j ) )
			{
				summ += sum[now][j];
				mn = min ( mn , minn[now][j] );
				now = fa[now][j];
			}
		} 
		printf ( "%lld %lld\n" , summ , mn );
	}

	return 0;
}
posted @ 2023-06-23 20:14  Echo_Long  阅读(106)  评论(0编辑  收藏  举报