【codeforces】Codeforces Round #278 (Div. 1) 题解

本文提供了 Codeforces Round #278 (Div. 1) 的五道题目详细解题思路与代码实现,包括 Fight the Monster 中的枚举与二分策略,Strip 中的线段树优化 DP 方法,Prefix Product Sequence 中的数学技巧,Conveyor Belts 中的分块与并查集应用,以及 Tourists 中的点双连通性和树链剖分技术。
摘要由CSDN通过智能技术生成

传送门:【codeforces】Codeforces Round #278 (Div. 1)


487A. Fight the Monster

枚举买的攻击力数以及防御力数,然后二分推出胜利需要的生命数,然后求一下需要的价格,在所有情况中取最小值。需要注意的是,攻击力可达到大约200,血量尽量设大一点。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

typedef long long LL ;

#define rep( i , a , b ) for ( int i = ( a ) ; i <  ( b ) ; ++ i )
#define For( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )
#define clr( a , x ) memset ( a , x , sizeof a )

const int MAXN = 100005 ;

int Y[3] , M[3] , a[3] ;

int search ( int atk , int def ) {
	int l = 0 , r = 10000 ;
	while ( l < r ) {
		int m = ( l + r ) >> 1 ;
		if ( ( m + Y[0] - 1 ) / ( M[1] - def ) > ( M[0] - 1 ) / ( atk - M[2] ) ) r = m ;
		else l = m + 1 ;
	}
	return l ;
}

void solve () {
	int ans = 10000000 ;
	scanf ( "%d%d%d" , &M[0] , &M[1] , &M[2] ) ;
	scanf ( "%d%d%d" , &a[0] , &a[1] , &a[2] ) ;
	For ( i , 0 , 200 ) {
		For ( j , 0 , 200 ) {
			int atk = Y[1] + i ;
			int def = Y[2] + j ;
			if ( atk - M[2] <= 0 ) continue ;
			if ( M[1] - def <= 0 ) {
				ans = min ( ans , i * a[1] + j * a[2] ) ;
				continue ;
			}
			ans = min ( ans , a[0] * search ( atk , def ) + a[1] * i + a[2] * j ) ;
		}
	}
	printf ( "%d\n" , ans ) ;
}

int main () {
	while ( ~scanf ( "%d%d%d" , &Y[0] , &Y[1] , &Y[2] ) ) solve () ;
	return 0 ;
}

487B. Strip

线段树优化DP。

设dp[i]为以第i个位置为一个集合的结尾时所需的最小集合数。

则我们用线段树维护0~i中的最大值以及最小值,二分这个集合的左端点,然后线段树判断是否最大值-最小值<=s,目的是让集合的左边解尽量靠左。

当得到集合的合法区间时,我们看这个内最小的dp值是多少,这个也可用线段树维护,然后返回最小值x,x+1就是位置i上的dp值。

当没有合法的方案时,dp[i]=INF。

最后我们判断一下dp[n]是否等于INF,等于则无解,否则答案就是dp[n]。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

typedef long long LL ;

#define rep( i , a , b ) for ( int i = ( a ) ; i <  ( b ) ; ++ i )
#define For( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )
#define clr( a , x ) memset ( a , x , sizeof a )
#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , m
#define rson rs , m + 1 , r
#define root 1 , 0 , n
#define mid ( ( l + r ) >> 1 )

const int MAXN = 100005 ;
const int INF = 0x3f3f3f3f ;

int a[MAXN] ;
int minv[MAXN << 2] ;
int maxv[MAXN << 2] ;
int dp[MAXN << 2] ;
int n , s , L ;

void build ( int o , int l , int r ) {
	if ( l == r ) {
		minv[o] = maxv[o] = a[l] ;
		return ;
	}
	int m = mid ;
	build ( lson ) ;
	build ( rson ) ;
	minv[o] = min ( minv[ls] , minv[rs] ) ;
	maxv[o] = max ( maxv[ls] , maxv[rs] ) ;
}

void update ( int x , int v , int o , int l , int r ) {
	if ( l == r ) {
		dp[o] = v ;
		return ;
	}
	int m = mid ;
	if ( x <= m ) update ( x , v , lson ) ;
	else update ( x , v , rson ) ;
	dp[o] = min ( dp[ls] , dp[rs] ) ;
}

int query_min , query_max ;
void query ( int L , int R , int o , int l , int r ) {
	if ( L <= l && r <= R ) {
		query_min = min ( query_min , minv[o] ) ;
		query_max = max ( query_max , maxv[o] ) ;
		return ;
	}
	int m = mid ;
	if ( L <= m ) query ( L , R , lson ) ;
	if ( m <  R ) query ( L , R , rson ) ;
}

int query_dp ( int L , int R , int o , int l , int r ) {
	if ( L <= l && r <= R ) return dp[o] ;
	int m = mid ;
	if ( R <= m ) return query_dp ( L , R , lson ) ;
	if ( m <  L ) return query_dp ( L , R , rson ) ;
	return min ( query_dp ( L , R , lson ) , query_dp ( L , R , rson ) ) ;
}

void solve () {
	a[0] = 0 ;
	clr ( dp , INF ) ;
	For ( i , 1 , n ) scanf ( "%d" , &a[i] ) ;
	build ( root ) ;
	update ( 0 , 0 , root ) ;
	For ( i , 1 , n ) {
		int l = 0 , r = i - 1 ;
		while ( l < r ) {
			int m = mid ;
			query_min = INF ;
			query_max = -INF ;
			query ( m + 1 , i , root ) ;
			if ( query_max - query_min <= s ) r = m ;
			else l = m + 1 ;
		}
		if ( l + L > i ) continue ;
		int tmp = query_dp ( l , i - L , root ) ;
		if ( tmp != INF ) update ( i , tmp + 1 , root ) ;
	}
	int ans = query_dp ( n , n , root ) ;
	printf ( "%d\n" , ans == INF ? -1 : ans ) ;
}

int main () {
	while ( ~scanf ( "%d%d%d" , &n , &s , &L ) ) solve () ;
	return 0 ;
}
487C. Prefix Product Sequence


这题我真心跪了……注意到如果存在(y+1)/y = (x+1)/x,那么易得y=x,当模数为素数的时候,这个是有意义的,同时也告诉我们,对于任意不同y,(y+1)/y各不相同。假设下一个要乘的数为ai,且要满足y*ai%n=(y+1),y表示的意义是:a1*a2*......*ai-1,由于(y+1)/y各不相同,所以ai也是各不相同的。

还有一种表达方式,数一说的时候说是前缀积:

i+1=1*(2/1)*(3/2)*...*((i+1)/i)

令x=(i+1)/i,因为1*(2/1)*(3/2)*...*(i/(i-1))=a1*a2*...*ai%n=i得到i*x%n=i+1,这样又和上面的形式一样了,由于(i+1)/i各不相同,所以x各不相同,注意x=(i+1)/i=(i+1)*inv[i],其中inv[i]为i关于n的乘法逆元。

n=4时候是有解的,当n>4时只有n为素数是才有解。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

typedef long long LL ;

#define rep( i , a , b ) for ( int i = ( a ) ; i <  ( b ) ; ++ i )
#define For( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )
#define clr( a , x ) memset ( a , x , sizeof a )

int n ;

int pow ( int a , int b , int mod ) {
	LL res = 1 , tmp = a ;
	while ( b ) {
		if ( b & 1 ) res = res * tmp % mod ;
		tmp = tmp * tmp % mod ;
		b >>= 1 ;
	}
	return res ;
}

void solve () {
	if ( n == 4 ) {
		printf ( "YES\n1\n3\n2\n4\n" ) ;
		return ;
	}
	for ( int i = 2 ; i * i <= n ; ++ i ) if ( n % i == 0 ) {
		printf ( "NO\n" ) ;
		return ;
	}
	printf ( "YES\n" ) ;
	if ( n == 1 ) printf ( "1\n" ) ;
	else if ( n == 2 ) printf ( "1\n2\n" ) ;
	else {
		printf ( "1\n2\n" ) ;
		rep ( i , 2 , n - 1 ) printf ( "%d\n" , ( LL ) pow ( i , n - 2 , n ) * ( i + 1 ) % n ) ;
		printf ( "%d\n" , n ) ;
	}
}

int main () {
	while ( ~scanf ( "%d" , &n ) ) solve () ;
	return 0 ;
}
487D. Conveyor Belts

这题我倒觉得比较简单= =

我看这题第一反应就是分块,每次只修改一个块,然后对块中的元素并查集。

这样修改最多sqrt(n)*m,查询为sqrt(n)。

#include <set>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

typedef long long LL ;

#define rep( i , a , b ) for ( int i = ( a ) ; i <  ( b ) ; ++ i )
#define For( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )
#define clr( a , x ) memset ( a , x , sizeof a )
#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , m
#define rson rs , m + 1 , r
#define root 1 , 1 , Graph.bcc_cnt
#define mid ( ( l + r ) >> 1 )

const int MAXN = 100005 ;
const int MAXM = 15 ;

struct Node {
	int x , y ;
	Node () {}
	Node ( int x , int y ) : x ( x ) , y ( y ) {}
} ;

Node f[MAXN][15] ;
char G[MAXN][15] ;
int n , m , q ;
int L ;

void go ( int x ) {
	int up = min ( n , x + L ) ;
	For ( i , x , up ) {
		For ( j , 1 , m ) {
			if ( j == 1 && G[i][j] == '<' ) {
				f[i][j] = Node ( i , 0 ) ;
			} else if ( j == m && G[i][j] == '>' ) {
				f[i][j] = Node ( i , m + 1 ) ;
			} else if ( G[i][j] == '^' ) {
				if ( i == x ) f[i][j] = Node ( i - 1 , j ) ;
				else f[i][j] = f[i - 1][j] ;
			} else if ( j < m ) {
				if ( G[i][j] == '>' && G[i][j + 1] == '<' ) {
					f[i][j] = Node ( -1 , -1 ) ;
					f[i][j + 1] = Node ( -1 , -1 ) ;
				}
			}
		}
		For ( j , 2 , m ) {
			if ( G[i][j] == '<' && G[i][j - 1] != '>' ) {
				f[i][j] = f[i][j - 1] ;
			}
		}
		rev ( j , m - 1 , 1 ) {
			if ( G[i][j] == '>' && G[i][j + 1] != '<' ) {
				f[i][j] = f[i][j + 1] ;
			}
		} 
	}
}

void solve () {
	int x , y ;
	char s[2] ;
	For ( i , 1 , n ) scanf ( "%s" , G[i] + 1 ) ;
	L = sqrt ( ( double ) n ) ;
	int N = ( n - 1 ) / L + 1 ;
	rep ( i , 0 , N ) go ( 1 + i * L ) ;
	while ( q -- ) {
		scanf ( "%s%d%d" , s , &x , &y ) ;
		if ( s[0] == 'A' ) {
			Node now = Node ( x , y ) ;
			while ( now.x >= 1 && now.y >= 1 && now.x <= n && now.y <= m ) {
				now = f[now.x][now.y] ;
			}
			printf ( "%d %d\n" , now.x , now.y ) ;
		} else {
			scanf ( "%s" , s ) ;
			G[x][y] = s[0] ;
			int pos = ( x - 1 ) / L * L + 1 ;
			go ( pos ) ;
		}
	}
}

int main () {
	while ( ~scanf ( "%d%d%d" , &n , &m , &q ) ) solve () ;
	return 0 ;
}


-----------------------------------------update-----------------------------------------


487E. Tourists

题解请戳这:487E. Tourists 点双连通+树链剖分


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值