Ancient Distance【整体二分】

2020牛客多校第四场A题


题意:

给一个有根树,在树上选择 k 个关键点(根必须选)

最小化点到最近关键祖先距离的最大值

求出 k 分别为 1,2,…,n 时答案的和


思路:

  对于我们要求某个答案ans下的最少需求,可以知道对于一个点,如果不是取的最后一个点,都可以取至少(ans + 1)个点,除去本身以外ans个点。于是乎,我们可以看到对于关键点个数有x的时候,答案个数大概会在\left \lceil \frac{n}{x} \right \rceil左右,那么,这里岂不是有一个类似于整数除法,那么,可以推测不同的关键点个数,可能答案是会相同的,所以我们不妨去处理出ans[i]表示答案为i时候,所使用的关键点的最多个数,那么ans[i - 1] - ans[i]就可以知道答案为i时候的种类数了。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
namespace fastIO {
#define BUF_SIZE 100000
    //fread -> read
    bool IOerror = 0;
    inline char nc() {
        static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
        if(p1 == pend) {
            p1 = buf;
            pend = buf + fread(buf, 1, BUF_SIZE, stdin);
            if(pend == p1) {
                IOerror = 1;
                return -1;
            }
        }
        return *p1++;
    }
    inline bool blank(char ch) {
        return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
    }
    inline bool read(int &x) {
        char ch;
        while(blank(ch = nc()));
        if(IOerror) return false;
        for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
        return true;
    }
#undef BUF_SIZE
};
using namespace fastIO;
const int maxN = 2e5 + 7;
int N, head[maxN], cnt, fa[maxN], deep[maxN], dfn[maxN], rid[maxN], tot, end_tim[maxN], max_deep;
vector<int> leave;
bool cmp(int e1, int e2) { return deep[e1] > deep[e2]; }
bool cmp_rid(int e1, int e2) { return deep[rid[e1]] > deep[rid[e2]]; }
struct Eddge
{
    int nex, to;
    Eddge(int a=-1, int b=0):nex(a), to(b) {}
} edge[maxN];
inline void addEddge(int u, int v)
{
    edge[cnt] = Eddge(head[u], v);
    head[u] = cnt++;
}
void dfs(int u)
{
    dfn[u] = ++tot; rid[tot] = u;
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        deep[v] = deep[u] + 1; max_deep = max(max_deep, deep[v]);
        dfs(v);
    }
    if(!~head[u]) leave.push_back(u);
    end_tim[u] = tot;
}
int ans[maxN], cop[maxN];
void cdq(int lenL, int lenR, int l, int r)
{
    if(lenL > lenR || l > r) return;
    if(l == r)
    {
        for(int i=lenL; i<=lenR; i++) ans[i] = l;
        return;
    }
    int mid = (lenL + lenR) >> 1;
    ans[mid] = 0;
    for(int i=1; i<=N; i++) cop[i] = deep[i];
    for(int i=N, u; i>=1; i--)
    {
        u = rid[i];
        if(cop[u] == deep[u] + mid || u == 1)
        {
            ans[mid]++;
            cop[u] = -1;
        }
        cop[fa[u]] = max(cop[fa[u]], cop[u]);
    }
    cdq(lenL, mid - 1, ans[mid], r);
    cdq(mid + 1, lenR, l, ans[mid]);
}
inline void init()
{
    cnt = max_deep = tot = 0;
    for(int i=1; i<=N; i++) { head[i] = -1; leave.clear(); }
}
int main()
{
    while(read(N))
    {
        init();
        for(int i=2; i<=N; i++) { read(fa[i]); addEddge(fa[i], i); }
        deep[1] = 0;
        dfs(1);
        cdq(0, max_deep, 1, N);
        int sum = 0;
        for(int i=1; i<=max_deep; i++) sum += i * (ans[i - 1] - ans[i]);
        printf("%d\n", sum);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41730082/article/details/107550664