传送门:【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 ; }
线段树优化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 点双连通+树链剖分