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

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

好久没写题解了,今天来补个好了……
这一场div1大概是我见到过的对我来说最容易的一场了……除了A题意略坑,C纠结了一会,其他题都是一眼秒……


A. Case of Matryoshkas
题意:一共有N个套娃,现在放成M堆,每一堆满足小的嵌套在大的里面,有个限制就是,一个套娃不能同时嵌套两个套娃。

然后每秒你能执行下列两个操作中的其中一个:
1.选择两个套娃A、B,将A放在B里面,需要满足B是单独一个的,即B里面没有套娃,且B不在别的套娃里面,A则需要满足不在别的套娃里面。
2.如果A被B直接套住,且B没被其他套娃套住,那么你能将套娃A从B里面取出。

问最少多少秒,你能将所有套娃合并成一堆,即1->2->3->……->n。

分析:根据题意,注意到只有和1连续的套娃,是可以不用拆分的,其他都是要完全拆除的,所以根据这个直接模拟就行了。

坑点:坑点只有未修改之前的题面了,之前表述我感觉不太清楚(导致我wa三发,每发都是不同的题意……),修改后题面就很清楚了……

my  code:

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

typedef long long LL ;

#define clr( a , x ) memset ( a , x , sizeof a )
#define rec( i , A , o ) for ( int i = A[o] ; i != o ; i = A[i] )

const int MAXN = 100005 ;
const int mod = 1e9 + 7 ;

int n , m ;
int a[MAXN] ;

void solve () {
    int ans = 0 , cnt = 0 ;
    for ( int i = 1 ; i <= m ; ++ i ) {
        int t , x ;
        scanf ( "%d" , &t ) ;
        for ( int j = 1 ; j <= t ; ++ j ) {
            scanf ( "%d" , &a[j] ) ;
        }
        if ( a[1] == 1 ) {
            int flag = 0 ;
            for ( int j = 2 ; j <= t ; ++ j ) {
                if ( a[j] - a[j - 1] != 1 ) {
                    ans += t - j + 1 ;
                    cnt += t - j + 2 ;
                    flag = 1 ;
                    break ;
                }
            }
            if ( !flag ) ++ cnt ;
        } else ans += t - 1 , cnt += t ;
    }
    printf ( "%d\n" , ans + cnt - 1 ) ;
}

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

B. Case of Fugitive

分析:考虑相邻两个陆地(l1,r1),(l2,r2),那么我们可以选用的桥的长度区间为[l2-r1,r2-l1],对于所有的n-1个这样的区间,我们该如何分配桥呢?
考虑两个得到的区间[x1,y1],[x2,y2]:
1.区间交叉,那么我们优先给y小的区间分配长度小的线段。
2.区间覆盖,那么我们优先给被覆盖的区间分配长度小的线段。
(YY一下,正确性很显然)
根据以上两条规则,我们只需要将得到的区间排序,先按右端点从小到大排序,然后右端点相同按照左端点从大到小排序。然后将桥插入到set,依次遍历区间,选取在这个区间内的长度最小的桥即可。

my  code:

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

typedef long long LL ;

#define clr( a , x ) memset ( a , x , sizeof a )
#define rec( i , A , o ) for ( int i = A[o] ; i != o ; i = A[i] )

const int MAXN = 200005 ;
const int mod = 1e9 + 7 ;

struct Node {
    LL l , r ;
    int idx ;
    bool operator < ( const Node& a ) const {
        if ( r !=a.r ) return r < a.r ;
        return l > a.l ;
    }
} ;

Node a[MAXN] ;
LL L[MAXN] , R[MAXN] ;
set < pair < LL , int > > s ;
set < pair < LL , int > > :: iterator it ;
LL ans[MAXN] ;
int n , m ;

void solve () {
    LL x ;
    s.clear () ;
    for ( int i = 1 ; i <= n ; ++ i ) {
        scanf ( "%I64d%I64d" , &L[i] , &R[i] ) ;
        if ( i > 1 ) {
            a[i - 1].l = L[i] - R[i - 1] ;
            a[i - 1].r = R[i] - L[i - 1] ;
            a[i - 1].idx = i - 1 ;
        }
    }
    for ( int i = 1 ; i <= m ; ++ i ) {
        scanf ( "%I64d" , &x ) ;
        s.insert ( make_pair ( x , i ) ) ;
    }
    sort ( a + 1 , a + n ) ;
    -- n ;
    for ( int i = 1 ; i <= n ; ++ i ) {
        if ( s.begin () == s.end () ) {
            printf ( "No\n" ) ;
            return ;
        }
        it = s.lower_bound ( make_pair ( a[i].l , 0 ) ) ;
        if ( it == s.end () || it->first > a[i].r ) {
            printf ( "No\n" ) ;
            return ;
        }
        ans[a[i].idx] = it->second ;
        s.erase ( it ) ;
    }
    printf ( "Yes\n" ) ;
    for ( int i = 1 ; i <= n ; ++ i ) {
        printf ( "%I64d%c" , ans[i] , i == n ? '\n' : ' ' ) ;
    }
}

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

C. Case of Chocolate

分析:我们可以选择维护两个线段树。首先将权值离散。一个线段树保存的是以行为下标,副对角线上的对应位置不能到达的最右列的下标。另一个线段树保存的是以列为下标,副对角线上的对应位置不能到达的最下行的下标。然后对于询问是’L’的,我们更新列线段树上对应区间的最大值(最大值恰好对应上面的信息),询问是’U’的同理。

my  code:

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

typedef long long LL ;

#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 mid ( ( l + r ) >> 1 )
#define root 1 , 1 , cnt

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

struct Node {
    int x , y , f ;
} ;

Node node[MAXN] ;
int a[MAXN] , cnt ;
int lazyL[MAXN << 2] ;
int lazyU[MAXN << 2] ;
int n , m ;

int unique ( int n ) {
    sort ( a + 1 , a + n + 1 ) ;
    int cnt = 1 ;
    for ( int i = 2 ; i <= n ; ++ i ) {
        if ( a[i] != a[cnt] ) a[++ cnt] = a[i] ;
    }
    return cnt ;
}

int search ( int x , int l = 1 , int r = cnt ) {
    while ( l < r ) {
        int m = ( l + r ) >> 1 ;
        if ( a[m] >= x ) r = m ;
        else l = m + 1 ;
    }
    return l ;
}

void build ( int o , int l , int r ) {
    lazyL[o] = lazyU[o] = 0 ;
    if ( l == r ) return ;
    int m = mid ;
    build ( lson ) ;
    build ( rson ) ;
}

void pushdown ( int o ) {
    if ( lazyL[o] ) {
        lazyL[ls] = max ( lazyL[ls] , lazyL[o] ) ;
        lazyL[rs] = max ( lazyL[rs] , lazyL[o] ) ;
        lazyL[o] = 0 ;
    }
    if ( lazyU[o] ) {
        lazyU[ls] = max ( lazyU[ls] , lazyU[o] ) ;
        lazyU[rs] = max ( lazyU[rs] , lazyU[o] ) ;
        lazyU[o] = 0 ;
    }
}

void update ( int L1 , int R1 , int v , int f , int o , int l , int r ) {
    if ( L1 <= l && r <= R1 ) {
        if ( f == 1 ) lazyU[o] = max ( lazyU[o] , v ) ;
        else lazyL[o] = max ( lazyL[o] , v ) ;
        return ;
    }
    int m = mid ;
    pushdown ( o ) ;
    if ( L1 <= m ) update ( L1 , R1 , v , f , lson ) ;
    if ( m <  R1 ) update ( L1 , R1 , v , f , rson ) ;
}

int query1 ( int x , int o , int l , int r ) {
    if ( l == r ) return lazyU[o] ;
    int m = mid ;
    pushdown ( o ) ;
    if ( x <= m ) return query1 ( x , lson ) ;
    else return query1 ( x , rson ) ;
}

int query0 ( int x , int o , int l , int r ) {
    if ( l == r ) return lazyL[o] ;
    int m = mid ;
    pushdown ( o ) ;
    if ( x <= m ) return query0 ( x , lson ) ;
    else return query0 ( x , rson ) ;
}

void solve () {
    char op[5] ;
    cnt = 0 ;
    a[++ cnt] = 1 ;
    for ( int i = 1 ; i <= m ; ++ i ) {
        scanf ( "%d%d%s" , &node[i].y , &node[i].x , op ) ;
        node[i].f = op[0] == 'U' ;
        a[++ cnt] = node[i].x ;
        a[++ cnt] = node[i].y ;
    }
    cnt = unique ( cnt ) ;
    build ( root ) ;
    for ( int i = 1 ; i <= m ; ++ i ) {
        int x = search ( node[i].x ) ;
        int y = search ( node[i].y ) ;
        if ( node[i].f == 1 ) {
            int t = query1 ( y , root ) ;
            printf ( "%d\n" , a[x] - a[t] ) ;
            update ( y , y , x , 1 , root ) ;
            //printf ( "%d %d %d\n" , t + 1 , x , y ) ;
            if ( t + 1 <= x ) update ( t + 1 , x , y , 0 , root ) ;
        } else {
            int t = query0 ( x , root ) ;
            printf ( "%d\n" , a[y] - a[t] ) ;
            update ( x , x , y , 0 , root ) ;
            //printf ( "%d %d %d\n" , t + 1 , y , x ) ;
            if ( t + 1 <= y ) update ( t + 1 , y , x , 1 , root ) ;
        }
    }
}

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


D. Case of a Top Secret

分析:其实就是暴力模拟,先将端点转移到他能遇到的最右端,然后再得到能到达的最左端,然后我们肯定会绕着这两个点转几圈,剩下的长度其实就是mod这两个点的距离。然后根据绕了多少次,确定转移到这两个点中的哪个点。重复上述步骤直到两个端点都是自己为止。由于mod次数最多log(L)(每次长度至少折半),于是算法复杂度为 O(ni=1log(li))=O(nlog(MAXL))

my  code:

#include <bits/stdc++.h>
using namespace std ;

typedef long long LL ;

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

const int MAXN = 200005 ;

pair < int , int > p[MAXN] ;
int idx[MAXN] ;
int n , m ;

int Lsearch ( int x , int l = 1 , int r = n ) {
    while ( l < r ) {
        int m = ( l + r ) >> 1 ;
        if ( p[m].first >= x ) r = m ;
        else l = m + 1 ;
    }
    return l ;
}

int Rsearch ( int x , int l = 1 , int r = n ) {
    while ( l < r ) {
        int m = ( l + r + 1 ) >> 1 ;
        if ( p[m].first <= x ) l = m ;
        else r = m - 1 ;
    }
    return l ;
}

void deal () {
    int x , l ;
    scanf ( "%d%d" , &x , &l ) ;
    x = idx[x] ;
    while ( 1 ) {
        int t = Rsearch ( p[x].first + l ) ;
        if ( p[t].first - p[x].first <= l ) {
            l -= p[t].first - p[x].first ;
            x = t ;
        }
        int A = Lsearch ( p[x].first - l ) ;
        if ( A == x ) {
            printf ( "%d\n" , p[x].second ) ;
            return ;
        }
        int cnt = l / ( p[x].first - p[A].first ) ;
        l %= ( p[x].first - p[A].first ) ;
        if ( cnt & 1 ) x = A ;
    }

}

void solve () {
    for ( int i = 1 ; i <= n ; ++ i ) {
        scanf ( "%d" , &p[i].first ) ;
        p[i].second = i ;
    }
    sort ( p + 1 , p + n + 1 ) ;
    for ( int i = 1 ; i <= n ; ++ i ) {
        idx[p[i].second] = i ;
    }
    for ( int i = 1 ; i <= m ; ++ i ) {
        deal () ;
    }
}

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

E. Case of Computer Network

分析:首先,如果是在一个边双连通分量的,我们可以将其构造成强连通分量,于是问题只在桥了。对于桥,简单的做法,就是对于操作a->b,我们可以对up[lca(a,b)]–,up[a]++,down[lca(a,b)]–,down[b]++,然后最后按照树的bfs序倒着还原树上的前缀和即可。如果最后一个点(表示一条边),既有被标记为上升,又有被标记为下降,那么就是不合法的,反之则是合法的。

然后这题我的做法比较傻逼……我用了一个树链剖分来实现了这一个过程……

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 )
#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , m
#define rson rs , m + 1 , r
#define mid ( ( l + r ) >> 1 )
#define root 1 , 1 , n

const int MAXN = 200005 ;
const int MAXE = 400005 ;

struct Edge {
    int v , n ;
    Edge () {}
    Edge ( int v , int n ) : v ( v ) , n ( n ) {}
} ;

struct Node {
    int u , v ;
} ;

struct BCC {
    Edge E[MAXE] ;
    int H[MAXN] , cntE ;
    int bcc[MAXN] , bcc_cnt ;
    int low[MAXN] , dfn[MAXN] , dfs_clock ;
    int S[MAXN] , top ;

    void init () {
        cntE = top = bcc_cnt = dfs_clock = 0 ;
        clr ( bcc , 0 ) ;
        clr ( dfn , 0 ) ;
        clr ( H , -1 ) ;
    }

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

    void tarjan ( int u , int fa ) {
        low[u] = dfn[u] = ++ dfs_clock ;
        S[top ++] = u ;
        int flag = 1 ;
        for ( int i = H[u] ; ~i ; i = E[i].n ) {
            int v = E[i].v ;
            if ( v == fa && flag ) {
                flag = 0 ;
                continue ;
            }
            if ( !dfn[v] ) {
                tarjan ( v , u ) ;
                low[u] = min ( low[u] , low[v] ) ;
            }
            else low[u] = min ( low[u] , dfn[v] ) ;
        }
        if ( low[u] == dfn[u] ) {
            ++ bcc_cnt ;
            while ( 1 ) {
                int v = S[-- top] ;
                bcc[v] = bcc_cnt ;
                if ( v == u ) break ;
            }
        }
    }

    void solve ( int n ) {
        for ( int i = 1 ; i <= n ; ++ i ) {
            if ( !dfn[i] ) tarjan ( i , -1 ) ;
        }
    }
} ;

Edge E[MAXE] ;
int H[MAXN] , cntE ;
Node e[MAXN] ;
BCC c ;
int top[MAXN] ;
int siz[MAXN] ;
int pre[MAXN] ;
int dep[MAXN] ;
int pos[MAXN] ;
int son[MAXN] ;
int tree_idx ;
int n , m , q ;
int p[MAXN] ;
int val[2][MAXN] ;
map < pair < int , int > , int > mp ;

void init () {
    cntE = 0 ;
    tree_idx = 0 ;
    clr ( pre , 0 ) ;
    clr ( dep , 0 ) ;
    clr ( H , -1 ) ;
}

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

void dfs ( int u ) {
    siz[u] = 1 ;
    son[u] = 0 ;
    for ( int i = H[u] ; ~i ; i = E[i].n ) {
        int v = E[i].v ;
        if ( v == pre[u] ) continue ;
        pre[v] = u ;
        dep[v] = dep[u] + 1 ;
        dfs ( v ) ;
        siz[u] += siz[v] ;
        if ( siz[son[u]] < siz[v] ) son[u] = v ;
    }
}

void rebuild ( int u , int top_element ) {
    top[u] = top_element ;
    pos[u] = ++ tree_idx ;
    if ( son[u] ) rebuild ( son[u] , top_element ) ;
    for ( int i = H[u] ; ~i ; i = E[i].n ) {
        int v = E[i].v ;
        if ( v != pre[u] && v != son[u] ) rebuild ( v , v ) ;
    }
}

void Update ( int x , int y ) {
    while ( top[x] != top[y] ) {
        if ( dep[top[x]] > dep[top[y]] ) {
            val[0][pos[top[x]]] ++ ;
            val[0][pos[x] + 1] -- ;
            x = pre[top[x]] ;
        } else {
            val[1][pos[top[y]]] ++ ;
            val[1][pos[y] + 1] -- ;
            y = pre[top[y]] ;
        }
    }
    if ( x == y ) return ;
    if ( dep[x] < dep[y] ) {
        val[1][pos[x] + 1] ++ ;
        val[1][pos[y] + 1] -- ;
    } else {
        val[0][pos[y] + 1] ++ ;
        val[0][pos[x] + 1] -- ;
    }
}

int find ( int x ) {
    return p[x] == x ? x : ( p[x] = find ( p[x] ) ) ;
}

void solve () {
    init () ;
    c.init () ;
    mp.clear () ;
    clr ( val , 0 ) ;
    for ( int i = 1 ; i <= m ; ++ i ) {
        scanf ( "%d%d" , &e[i].u , &e[i].v ) ;
        c.addedge ( e[i].u , e[i].v ) ;
        c.addedge ( e[i].v , e[i].u ) ;
    }
    c.solve ( n ) ;
    n = c.bcc_cnt ;
    for ( int i = 1 ; i <= n ; ++ i ) {
        p[i] = i ;
    }
    for ( int i = 1 ; i <= m ; ++ i ) {
        int u = c.bcc[e[i].u] ;
        int v = c.bcc[e[i].v] ;
        if ( u == v ) continue ;
        if ( u > v ) swap ( u , v ) ;
        if ( mp.count ( make_pair ( u , v ) ) ) continue ;
        mp[make_pair ( u , v )] = 1 ;
        addedge ( u , v ) ;
        addedge ( v , u ) ;
        p[find ( u )] = find ( v ) ;
    }
    for ( int i = 1 ; i <= n ; ++ i ) if ( !pre[i] ) {
        dfs ( i ) ;
        rebuild ( i , i ) ;
    }
    int ok = 1 ;
    for ( int i = 1 ; i <= q ; ++ i ) {
        int u , v ;
        scanf ( "%d%d" , &u , &v ) ;
        u = c.bcc[u] ;
        v = c.bcc[v] ;
        if ( u == v || !ok ) continue ;
        if ( find ( u ) != find ( v ) ) ok = 0 ;
        else Update ( u , v ) ;
    }
    for ( int i = 2 ; i <= n ; ++ i ) {
        val[0][i] += val[0][i - 1] ;
        val[1][i] += val[1][i - 1] ;
        if ( val[0][i] && val[1][i] ) ok = 0 ;
    }
    printf ( "%s\n" , ok ? "Yes" : "No" ) ;
}

int main () {
    while ( ~scanf ( "%d%d%d" , &n , &m , &q ) ) solve () ;
    return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值