传送门:【codeforces】Codeforces Round #278 (Div. 1)
枚举买的攻击力数以及防御力数,然后二分推出胜利需要的生命数,然后求一下需要的价格,在所有情况中取最小值。需要注意的是,攻击力可达到大约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 点双连通+树链剖分