CF#629 Div.3 E(LCA)F

E

質問の意味:木n個の点、あなたがパスを見つけることができるかどうか尋ね、そのルート上のいずれかのk点を、いずれかのパスの長さが1であるから考えます。

分析: LCA

#include <cstdioを> 
する#include <CStringの> 
する#include <cstdlib> 
の#include <cmath> 
の#include <アルゴリズム> 
の#include <ストリング> 
の#include <iostreamの> 
する#include <セット> 
の#include <マップ> 
の#include <積層> 
書式#include <キュー> 
の#include <ベクトル> 
の#include <sstream提供> 
typedefの長い長いLL。
const int型INF = 0x3f3f3f3f。
名前空間stdを使用。
const int型MAXN = 200000 + 10。
const int型LOG_MAXN = 18; 
constのダブルEPS = 1E-8; 
INT DCMP(ダブルA、ダブルB){ 
    IF(ファブ( - B)<EPS)戻り0; 
    <bを返しますか?-1:1; 
} 
ベクトル<整数> G [MAXN]。
整数N、M。
ベクトル<整数> V。
ボイドDFS(int型V、INT P、int型のD){ 
    深さ[V] = D。
    親[0] [V] = P。
    INT LEN = G [V] .size()。
    以下のために(INT I 0 =; I <LEN; ++ I){ 
        IF(G [V] [I] = P!){ 
            DFS(G [V] [i]は、V、D + 1)。
        } 
    } 
} 
ボイドのinit(){ 
    DFS(1、-1、0); 
    用(int型のk = 0; K + 1 <LOG_MAXN; ++ K){ 
        ための(int型V = 1; V <= N; ++ V){ 
            IF(親[K] [V] == -1)親[K + 1] [V] = -1。
            他{ 
                親[K + 1] [V] =親[K] [親[K] [V]]。
            } 
        } 
    } 
}
BOOL CMP(CONST INT&、CONST INT&B){ 
    戻り深さ[A] <深さ[B]。
} 
int型LCA(INT U、V INT){ 
    IF(深さ[U]>深さ[V]){ 
        スワップ(U、V)。
    } 
    (int型のk = 0; K <LOG_MAXN; ++ k)に対する{ 
        IF((深さ[U] -深さ[V])>> K&1){ 
            V =親[K] [V]。
        } 
    } 
    もし(U == V)Uを返します。
    { - (; K> = 0 --k 1のint K = LOG_MAXN)のための
        (親[K] [U] =親[K] [V]!){もし
            [U] U =親[K]。
            V =親[K] [V]。
        } 
    } 
    戻り親[0] [U]。
} 
int型のmain(){ 
    scanf関数( "%D%D"、&N、&M)。
    以下のために(INT I 0 =、I <N - 1; ++ I){
        scanf関数( "%D%D"、およびX&Y)。
        G [X] .push_back(Y)。
        G [Y] .push_back(X)。
    } 
    のinit(); 
    一方、(M - ){ 
        int型K。
        scanf関数( "%のD"、&K); 
        V.clear(); 
        一方、(K - ){ 
            scanf関数( "%のD"、&x)は、
            V.push_back(X)。
        } 
        ソート(V.begin()、V.end()、CMP)。
        INT LEN = V.size()。
        OK =真BOOL; 
        以下のために(INT I 1 =; I <LEN; ++ I){ 
            int型TMP = LCA(V [i]は、V [I - 1])。
            IF(深さ[V [I]] -深さ[TMP]> 1つの&&深さ[V [I - 1] -深さ[TMP]> 1){ 
                OK = FALSE;
            } 
        } 
        であれば(OK)のprintf( "YES \ n"); 
        他のprintf( "NO \ N"); 
    } 
    0を返します。
}

F

問題の意味: n個の点は、2つの操作を可能にする、ある:(1)最小の数プラス1(2)最大数から1を引いたものは、kの数に等しくすることができ、最小限の操作を数回尋ねます。

分析:トラバース列挙答えが最小値をとります。ばかりを右折、左に取ることができ、両側が取ります。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
#include<iostream>
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<vector>
#include<sstream>
typedef long long LL;
const int INF = 0x3f3f3f3f;
using namespace std;
const int MAXN = 200000 + 10;
const double eps = 1e-8;
int dcmp(double a, double b){
    if(fabs(a - b) < eps) return 0;
    return a < b ? -1 : 1;
}
LL a[MAXN], pre[MAXN], suf[MAXN];
map<LL, LL> l, r;
int main(){
    LL n, k;
    scanf("%lld%lld", &n, &k);
    for(LL i = 1; i <= n; ++i){
        scanf("%lld", &a[i]);
    }
    sort(a + 1, a + n + 1);
    for(LL i = 1; i <= n; ++i){
        pre[i] = pre[i - 1] + a[i];
        if(!l[a[i]]){
            l[a[i]] = i;
        }
    }
    for(LL i = n; i >= 1; --i){
        suf[i] = suf[i + 1] + a[i];
        if(!r[a[i]]) r[a[i]] = i;
    }
    LL ans = 1000000000000000;
    for(LL i = 1; i <= n; ++i){
        LL cnt = r[a[i]] - l[a[i]] + 1;
        if(cnt >= k){
            ans = 0;
            break;
        }
        LL cost1, cost2;
        cost1 = (a[i] - 1) * (i - 1) - pre[i - 1];
        cost2 = suf[r[a[i]] + 1] - (a[i] + 1) * (n - r[a[i]]);
        if(cnt + i - 1 >= k){
            ans = min(ans, cost1 + k - cnt);
        }
        if(cnt + n - r[a[i]] >= k){
            ans = min(ans, cost2 + k - cnt);
        }
        ans = min(ans, cost1 + cost2 + k - cnt);
        i += cnt - 1;
    }
    printf("%lld\n", ans);
    return 0;
}

  

おすすめ

転載: www.cnblogs.com/tyty-Somnuspoppy/p/12585038.html