Codeforces #446 (Div. 2) 题解


A Greed 

注意long long

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

const int maxn = 100005 ;
typedef long long ll ;
ll a[maxn] ;
ll b[maxn] ;

int main(){
    int n ;
    while(~ scanf("%d" , &n) ){
    priority_queue<ll> pq ;
    while(!pq.empty()) pq.pop() ;

    ll suma = 0 , p = 0 , q = 0 ;
    for(int i = 0 ; i < n ; i ++ ){
        scanf("%lld" , &a[i]) ;
        suma += a[i] ;

    }
    for(int i = 0 ; i < n ; i ++ ){
        scanf("%lld" , &b[i]) ;
        pq.push(b[i]) ;
    }
    p = pq.top() ;pq.pop() ;
    q = pq.top() ;

    if(p + q >= suma) puts("YES") ;
    else puts("NO") ;
    }
    return 0 ;
}

B  Wrath

用差分数组维护即可
#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 5 ;
typedef long long ll ;

int pre[maxn] ;

int main(){
    int n , a ;
    scanf("%d" , &n) ;
        memset(pre , 0 , sizeof(pre)) ;
        scanf("%d" , &a) ;
        for(int i = 2 ; i <= n ; i ++ ){

            scanf("%d" , &a) ;
            a = min(a , i - 1) ;
            pre[ i - a ] ++ ;
            pre[i] -- ;
        }
        int temp = 0 ;
        int ans = 0 ;

        for(int i = 1 ; i <= n ; i ++ ){
            temp += pre[i] ;
            if(temp > 0) ans ++ ;
        }
        printf("%d\n" , n - ans) ;

    return 0 ;
}

C  Pride 

暴力出gcd到1的最少层数,其他非1的数均可由1一步到达
注意开始时就有1的情况,记录开始时1的个数

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

const int maxn = 2005 ;
typedef long long ll ;
const int inf = 1e9 + 7 ;


int gcd(int m,int n){
    if(m==0) return n;
    if(n==0) return m;
    if(m%2==0&&n%2==0) return 2*gcd(m/2,n/2);
    else if(m%2==0) return gcd(m/2,n);
    else if(n%2==0) return gcd(m,n/2);
    else
        return gcd(min(m,n),fabs(m-n));
}

int a[maxn] ;
int d[maxn] ;
int main(){
    int n ;
    while(~ scanf("%d" , &n) ){
        for(int i = 1 ; i <= n ; i ++ ){
            scanf("%d" , &a[i]) ;
        }
        for(int i = 1 ; i <= n ; i ++ ){
            d[i] = inf ;
            int x = a[i] ;
            if(a[i] == 1)
                d[i] = i ;
            for(int j = i + 1 ; j < n + 1 ; j ++ ){
                x = gcd(x , a[j]) ;
                if(x == 1){
                    d[i] = j ;
                    break ;
                }
            }
        }

        if(d[1] == inf){
            puts("-1") ;

        }else{
            int s , m = inf ;
            int n1 = 0 ;
            for(int i = 1 ; i < n + 1 ; i ++ ){
                if(a[i] != 1)
                    n1 ++ ;
                if(d[i] - i < m ){
                    s = i ;
                    m = d[i] - i ;
                }
            }
            if(d[s] == s){
                printf("%d\n" , n1) ;
            }else{
                printf("%d\n" , d[s] - s + n1 - 1) ;
            }

        }
    }
    return 0 ;
}


D  Gluttony

思考,任意子区间和不相同,且原数列每一个数都不相同,所以只需将原来大小的排列顺序下移一位或上移一位即可

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

const int maxn = 105 ;

typedef pair<int , int> p2 ;
p2 a[maxn] ;
int ans[maxn] ;
int main(){
    int n , b ;
    scanf("%d" , &n) ;
    for(int i = 0 ; i < n ; i ++ ){
        scanf("%d" , &b) ;
        a[i] = make_pair(b , i) ;
    }
    sort(a , a + n) ;
    for(int i = 0 ; i < n - 1 ; i ++ ){
        ans[ a[i].second ] = a[i + 1].first ;
    }
    ans[ a[n - 1].second ] = a[0].first ;
    for(int i = 0 ; i < n; i ++ ){
        printf("%d " , ans[i]) ;
    }
    return 0 ;
}



E   Envy


生成树的询问问题,用离线并查集维护

由kruscal ,边按权值排列,我们按照权值分层,即权值相同的边为同一层。


注意:上一层的生成树无论怎么构成,都不影响下一层的生成树构成。


将所有的询问分层,如果某一询问中包含权值为w的边,即在map<int , vector<int> > 中插入该询问的id,同时记录询问的所有边。(也是按照权值分别存边, 即用map<int , vector<int> >  a[maxn], 在后面的每一层只询问权值为该层的边)

离线后,然后从最底层开始(即权值最小的层),遍历所有未被标记的询问在该层的边,即尝试建树,如果成环,即标记,break,如果未成环,说明该访问该层成立,撤销该询问在该层的所有操作(即并查集中对par修改操作,记录用两个vector记录,注意并查集的find操作也会修改par)该层结束后,对整幅图求出在该层的生成树,进行par修改。


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

const int maxn = 5e5 + 10 ;
typedef long long ll ;

struct edge{
    int st , ed , w ;
    bool operator < (const edge & k) const {
        return w < k.w ;
    }
};
int id[maxn] ;
edge E[maxn] ;

bool cmp(int x , int y){
    return E[x].w < E[y].w ;
}


int par[maxn] ;
bool ans[maxn] ;
map<int , vector<int> > ma , query[maxn] ;
//ma mean whether q has operation on w

///in order to recover


vector<int> pre_id ;
vector<int> pre_value ;

void init(int n){
    for(int i = 1 ; i <= n ; i ++ ) par[i] = i ;
    memset(ans , false , sizeof(ans)) ;
    pre_id.clear() ;
    pre_value.clear() ;
}

/*
int temp_unite(int son , int father){
    pre_id.push_back(son) ;
    pre_value.push_back(par[son]) ;

    par[son] = father ;
    return father ;
}

int find1(int x){
    int fa = par[x] ;
    while(fa != par[fa]){
        fa = par[ fa ] ;
    }

    if(x == fa) return x ;
    if(fa == par[x]) return fa ;
    pre_id.push_back(x) ;
    pre_value.push_back(par[x]) ;
    par[x] = fa ;
    return fa ;
}*/

int temp_unite(int x, int y){
    pre_id.push_back(x) ;
    pre_value.push_back(par[x]) ;
    par[x] = y ;
    return y;
}

int find1(int x){
    if (x == par[x]) return x;
    return temp_unite(x , find1(par[x]));
}
int find2(int x){
    if(par[x] == x) return x ;
    return par[x] = find2(par[x]) ;
}
void unite(int x , int y){
    int a = find2(x) ;
    int b = find2(y) ;
    if(par[a] == par[b]) return ;
    par[a] = b ;
    a = find2(x) , b = find2(y) ;
}
/// the process of find may change the array par
void reload(){
    for(int i = pre_id.size() - 1 ; i >= 0 ; i -- ){
        par[pre_id[i]] = pre_value[i] ;
    }
    pre_id.clear() ;
    pre_value.clear() ;
}
int main(){
    int n , m ;
    scanf("%d %d" , &n , &m) ;
    for(int i = 1 ; i <= m ; i ++ ){
        scanf("%d %d %d" , &E[i].st , &E[i].ed , &E[i].w) ;
        id[i] = i ;
    }
    int op ; scanf("%d" , &op) ;

    for(int i = 1 , k ; i <= op ; i ++ ){
        scanf("%d" , &k) ;
        for(int j = 1 , x ; j <= k ; j ++ ){
            scanf("%d" , &x) ;
            int w = E[x].w ;
            if(ma[w].size() == 0 || ma[w][ma[w].size() - 1] != i)
                ma[w].push_back(i) ;
            query[i][w].push_back(x) ;
        }
    }
    sort(id + 1 , id + m + 1 , cmp) ;
    init(n) ;

    for(int i = 1 ; i <= m ; i ++ ){
        int L = i , R = i ;
        while(E[id[R + 1]].w == E[id[L]].w && R + 1 <= m) R ++ ;
        int w = E[id[i]].w ;

        for(int j = 0 ; j < ma[w].size() ; j ++ ){
            int query_id = ma[w][j] ;
            if(ans[query_id]) continue ;

            for(int k = 0 ; k < query[query_id][w].size() ; k ++ ){
                int temp_edge = query[query_id][w][k] ;
                int st = E[temp_edge].st ;
                int ed = E[temp_edge].ed ;
                //cout << k << " " << query_id << st << " " <<ed  << "??";
                if(find1(st) != find1(ed))
                    temp_unite(find1(st) , find1(ed) ) ;
                else{
                    ans[query_id] = true ;
                    break ;
                }

            }

            reload() ;
        }

        for(int j = L ; j <= R ; j ++ ){
            int st = E[id[j]].st ;
            int ed = E[id[j]].ed ;

            /*if(find2(st) != find2(ed))
                temp_unite(find1(st) , find1(ed) ) ;*/
            unite(st , ed) ;
        }//for(int kk = 1 ; kk <= n ; kk ++ ) cout << par[kk] << " " ; cout << endl ;
        pre_id.clear() ;
        pre_value.clear() ;
        i = R ;
    }
    for(int i = 1 ; i <= op ; i ++ ){
        printf("%s\n" , ans[i] ? "NO" : "YES") ;
    }
    return 0 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值