CF #629 Div.3 E(LCA)F

E

题意:给定一棵n个点的树,问是否能找到一条路径,使k个点要么在路径上,要么距路径长度为1。

分析:LCA

#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 int LOG_MAXN = 18;
const double eps = 1e-8;
int dcmp(double a, double b){
    if(fabs(a - b) < eps) return 0;
    return a < b ? -1 : 1;
}
vector<int> G[MAXN];
int depth[MAXN];
int parent[LOG_MAXN][MAXN];
int n, m;
vector<int> V;
void dfs(int v, int p, int d){
    depth[v] = d;
    parent[0][v] = p;
    int len = G[v].size();
    for(int i = 0; i < len; ++i){
        if(G[v][i] != p){
            dfs(G[v][i], v, d + 1);
        }
    }
}
void init(){
    dfs(1, -1, 0);
    for(int k = 0; k + 1 < LOG_MAXN; ++k){
        for(int v = 1; v <= n; ++v){
            if(parent[k][v] == -1) parent[k + 1][v] = -1;
            else{
                parent[k + 1][v] = parent[k][parent[k][v]];
            }
        }
    }
}
bool cmp(const int &a, const int &b){
    return depth[a] < depth[b];
}
int lca(int u, int v){
    if(depth[u] > depth[v]){
        swap(u, v);
    }
    for(int k = 0; k < LOG_MAXN; ++k){
        if((depth[u] - depth[v]) >> k & 1){
            v = parent[k][v];
        }
    }
    if(u == v) return u;
    for(int k = LOG_MAXN - 1; k >= 0; --k){
        if(parent[k][u] != parent[k][v]){
            u = parent[k][u];
            v = parent[k][v];
        }
    }
    return parent[0][u];
}
int main(){
    scanf("%d%d", &n, &m);
    int x, y;
    for(int i = 0; i < n - 1; ++i){
        scanf("%d%d", &x, &y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    init();
    while(m--){
        int k;
        scanf("%d", &k);
        V.clear();
        while(k--){
            scanf("%d", &x);
            V.push_back(x);
        }
        sort(V.begin(), V.end(), cmp);
        int len = V.size();
        bool ok = true;
        for(int i = 1; i < len; ++i){
            int tmp = lca(V[i], V[i - 1]);
            if(depth[V[i]] - depth[tmp] > 1 && depth[V[i - 1]] - depth[tmp] > 1){
                ok = false;
                break;
            }
        }
        if(ok) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

F

题意:有n个点,允许进行两种操作:(1)将最小的数加1(2)将最大的数减一,问最少操作几次,可以得到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;
}

  

posted @ 2020-03-28 19:40  Somnuspoppy  阅读(130)  评论(0编辑  收藏  举报