【codeforces】Codeforces Round #311 (Div. 2)only 【题解】

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


A. Ilya and Diplomas

首先对n-min2-min3和max1取个最小值,第一个值就输出这个,然后n减去这个最小值。
接下来,对n-min3和max2取个最小值,第二个值就输出这个,然后n减去这个最小值。
最后输出n。
很显然这样是对的,因为我们走的每一步都是合法的。

my  code:

#include <stdio.h>
#include <string.h>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#pragma comment(linker, "/STACK:16777216")
using namespace std ;

typedef long long LL ;

#define clr( a , x ) memset ( a , x , sizeof a )

const int MAXN = 200005 ;

int n ;

void solve () {
    int L1 , L2 , L3 ;
    int R1 , R2 , R3 ;
    scanf ( "%d%d%d%d%d%d" , &L1 , &R1 , &L2 , &R2 , &L3 , &R3 ) ;
    int x1 = min ( R1 , n - L2 - L3 ) ;
    n -= x1 ;
    int x2 = min ( R2 , n - L3 ) ;
    n -= x2 ;
    int x3 = n ;
    printf ( "%d %d %d\n" , x1 , x2 , x3 ) ;
}

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

B. Pasha and Tea

排个序,然后前n个乘以2,然后取个最小值,乘上1.5后和w取个最小值,输出即可。

my  code:

#include <stdio.h>
#include <string.h>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#pragma comment(linker, "/STACK:16777216")
using namespace std ;

typedef long long LL ;

#define clr( a , x ) memset ( a , x , sizeof a )

const int MAXN = 200005 ;

int a[MAXN] ;
int n , w ;

void solve () {
    int x , minv = 1e9 ;
    for ( int i = 1 ; i <= 2 * n ; ++ i ) {
        scanf ( "%d" , &a[i] ) ;
    }
    sort ( a + 1 , a + n * 2 + 1 ) ;
    printf ( "%.6f\n" , min ( 0.0 + w , min ( a[1] * 2 , a[n + 1] ) * 1.5 * n ) ) ;
}

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

C. Arthur and Table

首先是枚举最大的长度L,设个数为X,然后每次我们将大于最大长度的都切掉,cost加上。然后小于这个长度的,我们保留花费最大的X-1个,如果不足X-1个就都保留,其余的切掉,cost加上,这里因为d只有200的限制,直接枚举d,然后d[i]<要切掉的,就cost加上d[i] * i,否则加上剩下要切的 * d[i]。这个长度枚举完后,更新答案,更新d[i]。

my  code:

#include <stdio.h>
#include <string.h>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#pragma comment(linker, "/STACK:16777216")
using namespace std ;

typedef long long LL ;

#define clr( a , x ) memset ( a , x , sizeof a )

const int MAXN = 100005 ;
const int MAXE = 200005 ;

vector < int > G[MAXN] ;
int a[MAXN] ;
int sum[MAXN] ;
int d[205] ;
int n ;

void solve () {
    int x ;
    clr ( sum , 0 ) ;
    clr ( d , 0 ) ;
    for ( int i = 1 ; i < MAXN ; ++ i ) {
        G[i].clear () ;
    }
    for ( int i = 1 ; i <= n ; ++ i ) {
        scanf ( "%d" , &a[i] ) ;
    }
    for ( int i = 1 ; i <= n ; ++ i ) {
        scanf ( "%d" , &x ) ;
        G[a[i]].push_back ( x ) ;
        sum[a[i]] += x ;
    }
    for ( int i = 1 ; i < MAXN ; ++ i ) {
        sort ( G[i].begin () , G[i].end () ) ;
        sum[i] += sum[i - 1] ;
    }
    int ans = 1e9 ;
    int num = 0 ;
    for ( int i = 1 ; i <= 100000 ; ++ i ) {
        if ( G[i].size () ) {
            int cost = sum[MAXN - 1] - sum[i] ;
            int t = max ( 0 , num - ( int ) G[i].size () + 1 ) ;
            for ( int j = 1 ; j <= 200 ; ++ j ) {
                if ( t > d[j] ) cost += d[j] * j , t -= d[j] ;
                else {
                    cost += j * t ;
                    break ;
                }
            }
            ans = min ( ans , cost ) ;
            for ( int j = 0 ; j < G[i].size () ; ++ j ) {
                d[G[i][j]] ++ ;
            }
            num += G[i].size () ;
        }
    }
    printf ( "%d\n" , ans ) ;
}

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

D. Vitaly and Cycle

四种情况:
1.m=0,此时用到3条边,因为是选三个点,所以方案数为 C3n .
2.一个点最多属于一条边,此时我们枚举边,再枚举不在边上的点,那么我们需要两条边将点和边相连构成三角形,方案数为m*(n-2).
3.存在奇环,此时一条边都不需要,由于不添加边,所以只有一种方案,就是原图.
4.不存在奇环,这时只需要连一条边,即二分图染色后,相同颜色的建边,YY一下你会发现这其实很正确,因为未建边前相同颜色的点中间必定经过奇数个点,偶数条边,再加一条边就可以构成奇环。假设我们进行二分图黑白染色后,黑色是x个,白色是y个,于是此时方案数就是,每个连通块内x*(x-1)+y*(y-1)的累加.

my  code:

#include <stdio.h>
#include <string.h>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#pragma comment(linker, "/STACK:16777216")
using namespace std ;

typedef long long LL ;

#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[MAXE] ;
int H[MAXN] , cntE ;
int deg[MAXN] ;
int vis[MAXN] ;
int U[MAXN] , V[MAXN] ;
int n , m ;
int one , two , odd ;

void init () {
    cntE = 0 ;
    clr ( H , -1 ) ;
    clr ( deg , 0 ) ;
    clr ( vis , 0 ) ;
}

void addedge ( int u , int v ) {
    E[cntE] = Edge ( v , H[u] ) ;
    H[u] = cntE ++ ;
}

void dfs ( int u ) {
    for ( int i = H[u] ; ~i ; i = E[i].n ) {
        int v = E[i].v ;
        if ( !vis[v] ) {
            vis[v] = 3 - vis[u] ;
            if ( vis[v] == 1 ) one ++ ;
            else two ++ ;
            dfs ( v ) ;
        }
        if ( vis[v] + vis[u] != 3 ) odd = 1 ;
    }
}

void solve () {
    int u , v ;
    if ( m == 0 ) {
        printf ( "3 %I64d\n" , ( LL ) n * ( n - 1 ) * ( n - 2 ) / 6 ) ;
        return ;
    }
    init () ;
    for ( int i = 1 ; i <= m ; ++ i ) {
        scanf ( "%d%d" , &u , &v ) ;
        U[i] = u ;
        V[i] = v ;
        addedge ( u , v ) ;
        addedge ( v , u ) ;
        deg[u] ++ ;
        deg[v] ++ ;
    }
    LL ans = 0 ;
    for ( int i = 1 ; i <= m ; ++ i ) {
        if ( deg[U[i]] == 1 && deg[V[i]] == 1 ) ans ++ ;
        else {
            ans = 0 ;
            break ;
        }
    }
    if ( ans ) {
        printf ( "2 %I64d\n" , ( LL ) ans * ( n - 2 ) ) ;
        return ;
    }
    odd = 0 ;
    for ( int i = 1 ; i <= n ; ++ i ) if ( !vis[i] ) {
        one = 1 ;
        two = 0 ;
        vis[i] = 1 ;
        dfs ( i ) ;
        if ( odd ) {
            printf ( "%d %d\n" , 0 , 1 ) ;
            return ;
        }
        ans += ( LL ) one * ( one - 1 ) / 2 + ( LL ) two * ( two - 1 ) / 2 ;
    }
    printf ( "1 %I64d\n" , ans ) ;
}

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

E. Ann and Half-Palindrome

首先,先说明下,我是十分十分十分暴力的做的,在不考虑空间复杂度的前提下……

一开始我们预处理出[L,R]是不是半回文串(即G[L][R]=1表示[L,R]构成了半回文串,反之G[L][R]=0表示没有构成),这个只要选择一个点或者一对点,按照规则扩展就行了,空间复杂度和时间复杂度均为 O(N2)

然后我们将以下标i开始的后缀都插入到字典树,空间复杂度和时间复杂度均为 O(N2) 。为了之后的操作方便,我们需要保存每个节点的siz值,也就是从这个节点开始往下走一共有多少个半回文串(前缀是当前节点表示的串的个数)。为了不用dfs整棵字典树,我们对得到的G[L][R]做一遍后缀和,这样添加串到字典树的时候,每个对应节点加上对应值即可。

接下来只要在字典树上模拟就行了^_^,看是不是在当前节点停止,或者要往左或者往右走。这里时间复杂度是 O(N2)

总的空间复杂度大的不行= =但是没关系,cf壕气冲天,给了512MB,我用了392MB……跑了170ms,只能说cf服务器太好= =

my  code:

#include <stdio.h>
#include <string.h>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
using namespace std ;

typedef long long LL ;

#define clr( a , x ) memset ( a , x , sizeof a )

const int MAXN = 5005 ;

int nxt[MAXN * MAXN][2] ;
int siz[MAXN * MAXN] ;
char s[MAXN] ;
int G[MAXN][MAXN] ;
int root , cur ;
int k ;

int newnode () {
    nxt[cur][0] = nxt[cur][1] = 0 ;
    siz[cur] = 0 ;
    return cur ++ ;
}

void insert ( char s[] , int x , int y ) {
    int now = root ;
    siz[root] += G[x][y] ;
    for ( int i = 0 ; s[i] ; ++ i ) {
        int idx = s[i] == 'b' ;
        if ( nxt[now][idx] == 0 ) nxt[now][idx] = newnode () ;
        now = nxt[now][idx] ;
        siz[now] += G[x][y] ;
        ++ y ;
    }
}

void query ( int k ) {
    int now = root ;
    while ( 1 ) {
        int L = nxt[now][0] ;
        int R = nxt[now][1] ;
        int num = siz[now] - siz[L] - siz[R] ;
        //printf ( "%d %d %d\n" , siz[now] , siz[L] , siz[R] ) ;
        if ( k <= num ) break ;
        k -= num ;
        //getchar () ;
        //printf ( "%d\n" , k ) ;
        if ( k <= siz[L] ) {
            printf ( "a" ) ;
            now = L ;
        } else {
            printf ( "b" ) ;
            k -= siz[L] ;
            now = R ;
        }
    }
    printf ( "\n" ) ;
}

void solve () {
    cur = 1 ;
    root = newnode () ;
    int n = strlen ( s ) ;
    clr ( G , 0 ) ;
    for ( int i = 0 ; i < n ; ++ i ) {
        for ( int L = i , R = i ; L >= 0 && R < n ; L -= 2 , R += 2 ) {
            if ( s[L] == s[R] ) G[L][R] = 1 ;
            else break ;
        }
    }
    for ( int i = 0 ; i < n ; ++ i ) {
        for ( int L = i - 1 , R = i + 1 ; L >= 0 && R < n ; L -= 2 , R += 2 ) {
            if ( s[L] == s[R] ) G[L][R] = 1 ;
            else break ;
        }
    }
    for ( int i = 0 ; i < n ; ++ i ) {
        for ( int L = i - 2 , R = i + 1 ; L >= 0 && R < n ; L -= 2 , R += 2 ) {
            if ( s[L] == s[R] ) G[L][R] = 1 ;
            else break ;
        }
    }
    for ( int i = 0 ; i < n ; ++ i ) {
        for ( int L = i , R = i + 1 ; L >= 0 && R < n ; L -= 2 , R += 2 ) {
            if ( s[L] == s[R] ) G[L][R] = 1 ;
            else break ;
        }
    }
    /*for ( int i = 0 ; i < n ; ++ i ) {
        for ( int j = 0 ; j < n ; ++ j ) {
            printf ( "%d " , G[i][j] ) ;
        }
        printf ( "\n" ) ;
    }*/
    for ( int i = 0 ; i < n ; ++ i ) {
        for ( int j = n - 1 ; j >= i ; -- j ) {
            G[i][j - 1] += G[i][j] ;
        }
    }
    for ( int i = 0 ; i < n ; ++ i ) {
        insert ( s + i , i , i ) ;
    }
    //printf ( "%d\n" , siz[root] ) ;
    scanf ( "%d" , &k ) ;
    query ( k ) ;
}

int main () {
    while ( ~scanf ( "%s" , s ) ) solve () ;
    return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值