【codeforces】Codeforces Round #277.5 (Div. 2)

来源:互联网 发布:东方网络最新公告 编辑:程序博客网 时间:2024/06/05 17:02

传送门:【codeforces】Codeforces Round #277.5 (Div. 2)


489A. SwapSort

选择排序!绝对的不超过n次交换。

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>//#include <cmath>using namespace std ;typedef long long LL ;#pragma comment ( linker , "/STACK:1024000000" )#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 rec( i , A , o ) for ( int i = A[o] ; i != o ; i = A[i] )#define clr( a , x ) memset ( a , x , sizeof a )const int MAXN = 100005 ;const int MAXE = 200005 ;const int INF = 0x3f3f3f3f ;struct Node {int x , y ;Node () {}Node ( int x , int y ) : x ( x ) , y ( y ) {}} ;Node ans[MAXN] ;int a[MAXN] ;int top ;int n ;void solve () {top = 0 ;rep ( i , 0 , n ) scanf ( "%d" , &a[i] ) ;rep ( i , 0 , n ) {int tmp = a[i] , pos = i ;rep ( j , i + 1 , n ) if ( a[j] < tmp ) {tmp = a[j] ;pos = j ;}if ( pos != i ) {ans[top ++] = Node ( i , pos ) ;a[pos] = a[i] ;}}printf ( "%d\n" , top ) ;rep ( i , 0 , top ) printf ( "%d %d\n" , ans[i].x , ans[i].y ) ;}int main () {while ( ~scanf ( "%d" , &n ) ) solve () ;return 0 ;}

489B. BerSU Ball

我用的二分匹配,对于可以匹配的建边,然后跑最大匹配即可。

实际上可以排序贪心。

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>//#include <cmath>using namespace std ;typedef long long LL ;#pragma comment ( linker , "/STACK:1024000000" )#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 rec( i , A , o ) for ( int i = A[o] ; i != o ; i = A[i] )#define clr( a , x ) memset ( a , x , sizeof a )const int MAXN = 100005 ;const int MAXE = 200005 ;struct Edge {int v , n ;Edge () {}Edge ( int v , int n ) : v ( v ) , n ( n ) {}} ;Edge E[MAXN] ;int H[MAXN] , cntE ;int link[MAXN] ;int vis[MAXN] ;int a[MAXN] ;int b[MAXN] ;int n , m ;void clear () {cntE = 0 ;clr ( H , -1 ) ;}void addedge ( int u , int v ) {E[cntE] = Edge ( v , H[u] ) ;H[u] = cntE ++ ;}int find ( int u ) {for ( int i = H[u] ; ~i ; i = E[i].n ) if ( !vis[E[i].v] ) {int v = E[i].v ;vis[v] = 1 ;if ( link[v] == -1 || find ( link[v] ) ) {link[v] = u ;return 1 ;}}return 0 ;}int match () {clr ( link , -1 ) ;int ans = 0 ;For ( i , 1 , n ) {clr ( vis , 0 ) ;ans += find ( i ) ;}return ans ;}void solve () {clear () ;For ( i , 1 , n ) scanf ( "%d" , &a[i] ) ;scanf ( "%d" , &m ) ;For ( i , 1 , m ) scanf ( "%d" , &b[i] ) ;For ( i , 1 , n ) {For ( j , 1 , m ) if ( abs ( a[i] - b[j] ) <= 1 ) addedge ( i , j ) ;}printf ( "%d\n" , match () ) ;}int main () {while ( ~scanf ( "%d" , &n ) ) solve () ;return 0 ;}

489C. Given Length and Sum of Digits...

易知最大的是从高位加起,最小的是从低位加起,注意1 0这样的情况。

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>//#include <cmath>using namespace std ;typedef long long LL ;#pragma comment ( linker , "/STACK:1024000000" )#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 rec( i , A , o ) for ( int i = A[o] ; i != o ; i = A[i] )#define clr( a , x ) memset ( a , x , sizeof a )const int MAXN = 105 ;int a[MAXN] ;int b[MAXN] ;int m , s ;void solve () {if ( m > 1 && s < 1 || s > 9 * m ) {printf ( "-1 -1\n" ) ;return ;} else if ( m == 1 && s == 0 ) {printf ( "0 0\n" ) ;return ;}For ( i , 1 , m ) a[i] = b[i] = 0 ;int ss = s ;For ( i , 1 , m ) {//maxif ( 9 - a[i] < ss ) {ss -= 9 - a[i] ;a[i] = 9 ;} else {a[i] = ss ;break ;}}ss = s ;rev ( i , m , 1 ) {//minif ( 9 - b[i] < ss ) {ss -= 9 - b[i] ;b[i] = 9 ;} else {b[i] = ss - 1 ;b[1] = b[1] + 1 ;break ;}}For ( i , 1 , m ) printf ( "%d" , b[i] ) ;printf ( " " ) ;For ( i , 1 , m ) printf ( "%d" , a[i] ) ;printf ( "\n" ) ;}int main () {while ( ~scanf ( "%d%d" , &m , &s ) ) solve () ;return 0 ;}
489D. Unbearable Controversy of Being

以每个点为起点bfs一次,找到起点到每个距离起点2的点的路径数x,ans+=x*(x-1)/2。易知最多每次只会走m条边,所以复杂度为o(nm)。

PS:实际上根本不需要bfs,用邻接表判断就好了。

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>//#include <cmath>using namespace std ;typedef long long LL ;#pragma comment ( linker , "/STACK:1024000000" )#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 rec( i , A , o ) for ( int i = A[o] ; i != o ; i = A[i] )#define clr( a , x ) memset ( a , x , sizeof a )const int MAXN = 3003 ;const int MAXE = 30005 ;struct Edge {    int v , n ;    Edge () {}    Edge ( int v , int n ) : v ( v ) , n ( n ) {}} ;struct Node {    int x , d ;    Node () {}    Node ( int x , int d ) : x ( x ) , d ( d ) {}} ;Node Q[MAXE] ;Edge E[MAXE] ;int head , tail ;int H[MAXN] , cntE ;LL G[MAXN][MAXN] ;int n , m ;void clear () {    cntE = 0 ;    clr ( H , -1 ) ;}void addedge ( int u , int v ) {    E[cntE] = Edge ( v , H[u] ) ;    H[u] = cntE ++ ;}void bfs ( int root ) {    head = tail = 0 ;    Q[tail ++] = Node ( root , 0 ) ;    while ( head != tail ) {        Node o = Q[head ++] ;        if ( head == MAXE ) head = 0 ;        int u = o.x ;        int d = o.d ;        if ( d == 2 ) {            G[root][u] ++ ;            continue ;        }        for ( int i = H[u] ; ~i ; i = E[i].n ) {            int v = E[i].v ;            if ( v == root ) continue ;            Q[tail ++] = Node ( v , d + 1 ) ;        }    }}void solve () {    int u , v ;    clear () ;    clr ( G , 0 ) ;    rep ( i , 0 , m ) {        scanf ( "%d%d" , &u , &v ) ;        addedge ( u , v ) ;    }    For ( i , 1 , n ) bfs ( i ) ;    LL ans = 0 ;    For ( i , 1 , n ) For ( j , 1 , n ) ans += G[i][j] * ( G[i][j] - 1 ) / 2 ;    printf ( "%I64d\n" , ans ) ;}int main () {    while ( ~scanf ( "%d%d" , &n , &m ) ) solve () ;    return 0 ;}

489E. Hiking

题意:一个旅行者想从河的起点(坐标0)走到河的终点(编号n)途中有n个休息站(包括终点),休息站有2个属性:(1)坐标xi,(2)风景价值vi。每次只能从一个休息站走到另一个休息站,中间不能停留,旅行者想每次走L距离,但是这基本呢不可能,于是给了这样的一个定义:每次走ri的距离(即选择的两休息站之间的距离),则他会感到失意,失意指数为sqrt ( | ri - L | )。现在你的任务是选出一些休息站,使得(失意指数之和/休息站的风景价值之和)最小。起点为0,终点只能为n。休息站的坐标按照输入顺序严格递增给出。

分析:

考虑设r = sum { sqrt ( | ri - L | ) } / sum { vi }。

转化一下就成了sum { sqrt ( | ri - L | ) } - r * sum { vi } = 0。

令G(r)= sum { sqrt ( | ri - L | ) } - r * sum { vi }

我们的目标是最小化G(r),最小化G(r)等同于最小化r。

如果G(r)< 0,说明r取大了,但是sum { sqrt ( | ri - L | ) } / sum { vi } <= r,r可以更优!

如果G(r)= 0,此时r恰是最优值。

如果G(r)> 0,说明r取小了,sum { sqrt ( | ri - L | ) } / sum { vi }不可能比r小。

如果G(r)是一个单调递减函数,那么我们便可以用二分的方法去求得最优的r了,

当G(r)< 0时调整上界,

当G(r)>0时调整下界,

当二分上下界r-l<eps时大致认为此时的r即最优解。

G(r)函数的最小值求解可用O(n^2)的dp求解。

dp[i] = min { dp[j] + sqrt ( | x[i] - x[j] - L | ) - r * a[i] }。

幸运的是,G(r)的确是一个单调递减函数,所以二分是可行的。

关于本题算法正确性的证明,应该去看一下分数规划问题,本题就是一个分数规划问题,然后用dp求G(r)函数。


#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <cmath>using namespace std ;typedef long long LL ;#pragma comment ( linker , "/STACK:1024000000" )#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 rec( i , A , o ) for ( int i = A[o] ; i != o ; i = A[i] )#define clr( a , x ) memset ( a , x , sizeof a )const int MAXN = 1005 ;const double eps = 1e-8 ;const double INF = 1e30 ;double dp[MAXN] ;int x[MAXN] , b[MAXN] ;double a[MAXN] ;int pre[MAXN] ;int S[MAXN] , top ;int n , m ;void solve () {a[0] = x[0] = b[0] = 0 ;For ( i , 1 , n ) scanf ( "%d%d" , &x[i] , &b[i] ) ;double l = 0 , r = 1e10 ;while ( l < r - eps ) {double mid = ( l + r ) / 2 ;For ( i , 1 , n ) a[i] = mid * b[i] ;dp[0] = 0 ;For ( i , 1 , n ) {dp[i] = INF ;rev ( j , i - 1 , 0 ) {double tmp = dp[j] + sqrt ( double ( abs ( x[i] - x[j] - m ) ) ) - a[i] ;if ( dp[i] > tmp ) dp[i] = tmp , pre[i] = j ;}}if ( dp[n] > -eps ) l = mid ;else r = mid ;}top = 0 ;while ( n ) {S[top ++] = n ;n = pre[n] ;}rev ( i , top - 1 , 0 ) printf ( "%d%c" , S[i] , i ? ' ' : '\n' ) ;}int main () {while ( ~scanf ( "%d%d" , &n , &m ) ) solve () ;return 0 ;}

489F. Special Matrices

我是O(n^3)暴力的。。不要嘲笑我T  T

首先可以预处理出每一列还剩多少1可以用,用a[i]表示。

dp[i][j][k]表示已经处理到第i列,已经有j行放置了两个1,同时有k行已经放置了一个1。

当a[i] = 0时:

dp[i][j][k] = dp[i - 1][j][k]

当a[i] = 1时:

dp[i][j][k + 1] += ( n - m - j - k ) * dp[i - 1][j][k](增加一个1行)

    当k > 0:

    dp[i][j + 1][k - 1] += k * dp[i - 1][j][k](将一个1行变为2行)

当a[i] = 2时:

dp[i][j][k + 2] += ( n - m - j - k ) * ( n - m - j - k - 1 ) / 2 * dp[i - 1][j][k](增加两个1行)

    当k > 0:

    dp[i][j + 1][k] += ( n - m - j - k ) * k * dp[i - 1][j][k](将一个1行变为2行,同时再增加一个1行)

    当k > 1:

    dp[i][j +2][k - 2] += k * ( k - 1 ) / 2 * dp[i - 1][j][k](将两个1行变为2行)


这就是O(n^3)的算法,稍微优化一下,156ms。。。


正确的姿势应该是O(n^2)的算法,等我有空去想想。


#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>//#include <cmath>using namespace std ;typedef long long LL ;#pragma comment ( linker , "/STACK:1024000000" )#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 rec( i , A , o ) for ( int i = A[o] ; i != o ; i = A[i] )#define clr( a , x ) memset ( a , x , sizeof a )const int MAXN = 505 ;int mod ;int dp[2][MAXN][MAXN] ;int a[MAXN] ;int n , m ;inline void add ( int& x , int y ) {x += y ;if ( x >= mod ) x -= mod ;}void solve () {int x , cur = 0 ;clr ( dp , 0 ) ;For ( i , 1 , n ) a[i] = 2 ;For ( i , 1 , m ) For ( j , 1 , n ) {scanf ( "%1d" , &x ) ;a[j] -= x ;}dp[0][0][0] = 1 ;int s = n - m ;For ( i , 1 , n ) {cur ^= 1 ;clr ( dp[cur] , 0 ) ;For ( j , 0 , s ) {if ( j > i * 2 ) break ;For ( k , 0 , s - j ) if ( dp[cur ^ 1][j][k] ) {if ( a[i] == 0 ) add ( dp[cur][j][k] , dp[cur ^ 1][j][k] ) ;else if ( a[i] == 1 ) {if ( k > 0 ) add ( dp[cur][j + 1][k - 1] , ( LL ) k * dp[cur ^ 1][j][k] % mod ) ;add ( dp[cur][j][k + 1] , ( LL ) ( s - k - j ) * dp[cur ^ 1][j][k] % mod ) ;} else {if ( k > 1 ) add ( dp[cur][j + 2][k - 2] , ( LL ) k * ( k - 1 ) / 2 * dp[cur ^ 1][j][k] % mod ) ;if ( k > 0 ) add ( dp[cur][j + 1][k] , ( LL ) ( s - k - j ) * k * dp[cur ^ 1][j][k] % mod ) ;add ( dp[cur][j][k + 2] , ( LL ) ( s - k - j ) * ( s - k - j - 1 ) / 2 * dp[cur ^ 1][j][k] % mod ) ;}}}}printf ( "%d\n" , dp[cur][n - m][0] ) ;}int main () {while ( ~scanf ( "%d%d%d" , &n , &m , &mod ) ) solve () ;return 0 ;}


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

F题O(n^2)解法。

设dp[i][j]表示已经有i列上有两个1,j列上有1个1,剩下的n-i-j列上还没有1。

那么,由于i的不递减,所以我们可以将这个作为递推的第一维。

dp[i][j + 2] += ( n - i - j ) * ( n - i - j - 1 ) / 2 * dp[i][j].(选择没有1的两列将其变成有一个1的两列)

dp[i + 1][j] += j * ( n - i - j ) * dp[i][j].(j>0)(选择一列有一个1的变成有两个1的,同时选择没有1的变成有一个1的)

dp[i + 2][j - 2] += j * ( j - 1 ) / 2 * dp[i][j].(j>1)(选择有一个1的两列变成有两个1的两列)

这样推法的可行性在于充分利用1的数量是固定的隐性条件,到最后终点dp[n][0]时的一定是合法的。

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>//#include <cmath>using namespace std ;typedef long long LL ;#pragma comment ( linker , "/STACK:1024000000" )#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 rec( i , A , o ) for ( int i = A[o] ; i != o ; i = A[i] )#define clr( a , x ) memset ( a , x , sizeof a )const int MAXN = 505 ;int mod ;int dp[MAXN][MAXN] ;int a[MAXN] ;int n , m ;inline void add ( int& x , int y ) {x += y ;if ( x >= mod ) x -= mod ;}void solve () {int v , cur = 0 ;clr ( dp , 0 ) ;clr ( a , 0 ) ;For ( i , 1 , m ) {getchar () ;For ( j , 1 , n ) a[j] += getchar () - '0' ;}int x = 0 , y = 0 ;For ( i , 1 , n ) {if ( a[i] == 2 ) ++ x ;else if ( a[i] == 1 ) ++ y ;}dp[x][y] = 1 ;For ( i , x , n ) {For ( j , 0 , n - i ) if ( dp[i][j] ) {add ( dp[i][j + 2] , ( LL ) ( n - i - j ) * ( n - i - j - 1 ) / 2 * dp[i][j] % mod ) ;if ( j > 0 ) add ( dp[i + 1][j] , ( LL ) j * ( n - i - j ) * dp[i][j] % mod ) ;if ( j > 1 ) add ( dp[i + 2][j - 2] , ( LL ) j * ( j - 1 ) / 2 * dp[i][j] % mod ) ;}}printf ( "%d\n" , dp[n][0] ) ;}int main () {while ( ~scanf ( "%d%d%d" , &n , &m , &mod ) ) solve () ;return 0 ;}


0 0
原创粉丝点击