1. 程式人生 > >【LOJ】#2268. 「SDOI2017」蘋果樹

【LOJ】#2268. 「SDOI2017」蘋果樹

include emp efi enter line mes 做出 我們 tdi

題解

顯然權值都是正的,我們最深的那個點一定延伸到了某個葉子

我們拋去這條鏈之外再選K個點即可

如果直接對一棵樹選K個點,滿足這樣的依賴關系,可以通過一個後序遍歷的順序做出來
轉移方法是
\(dp[i][j] = dp[i - 1][k] + (j - k) * v\)
或者
\(dp[i][j] = dp[i - siz[u]][j]\)
代表這個點選或者不選

我們把每個點拆成1和a[i] - 1兩個點,然後做兩次兒子遍歷順序恰好相反的dp

我們枚舉一個葉子的時候,在這個點右側這兩個後序遍歷重合的地方只有這個葉子到根所有點,這也是我們要必選的點

然後我們用兩個遍歷中這個點左側的點集,枚舉每個點集選幾個,來更新答案即可

代碼

#include <bits/stdc++.h>
#define enter putchar(‘\n‘)
#define space putchar(‘ ‘)
#define fi first
#define se second
#define MAXN 40005
//#define ivorysi
#define pii pair<int,int>
#define pb push_back
using namespace std;
typedef long long int64;
template<class T>
void read(T &res) {
    res = 0;char c = getchar();T f = 1;
    while(c < ‘0‘ || c > ‘9‘) {
        if(c == ‘-‘) f = -1;
        c = getchar();
    }
    while(c >= ‘0‘ && c <= ‘9‘) {
        res = res * 10 + c - ‘0‘;
        c = getchar();
    }
    res *= f;
}

template<class T>
void out(T x) {
    if(x < 0) {putchar(‘-‘);x = -x;}
    if(x >= 10) out(x / 10);
    putchar(‘0‘ + x % 10);
}
struct node {
    int to,next;
}E[MAXN * 2];
int64 tot;
int N,K,head[MAXN],sumE,fa[MAXN],Ncnt,v[MAXN],a[MAXN],sumv[MAXN];
int siz[MAXN],dep[MAXN],LA[MAXN],posA[MAXN],LB[MAXN],posB[MAXN],idx;
int f[51000005],g[51000005],Q1[500005],Q2[500005],ql,qr;
bool lef[MAXN];
void add(int u,int v) {
    E[++sumE].to = v;
    E[sumE].next = head[u];
    head[u] = sumE;
}
void dfsa(int u) {
    siz[u] = 1;dep[u] = dep[fa[u]] + 1;sumv[u] = sumv[fa[u]] + v[u];
    for(int i = head[u] ; i ; i = E[i].next) {
        int v = E[i].to;
        dfsa(v);siz[u] += siz[v];
    }
    LA[++idx] = u;posA[u] = idx;
}
void dfsb(int u) {
    vector<int> son;son.clear();
    for(int i = head[u] ; i ; i = E[i].next) {
        int v = E[i].to;son.pb(v);
    }
    reverse(son.begin(),son.end());
    for(int i = 0 ; i < son.size() ; ++i) dfsb(son[i]);
    LB[++idx] = u;posB[u] = idx;
}
void DP(int *L,int *p,int *f) {
    for(int i = 1 ; i <= Ncnt ; ++i) {
        int u = L[i];
        int *f1 = f + (i - 1) * (K + 1),*f2 = f + i * (K + 1);
        memcpy(f2,f + (p[u] - siz[u]) * (K + 1),sizeof(int) * (K + 1));
        if(u <= N) {
            for(int j = 1 ; j <= K ; ++j) f2[j] = max(f2[j],f1[j - 1] + v[u]);
        }
        else {
            Q1[ql = qr = 1] = 0;Q2[1] = 0;
            for(int j = 1 ; j <= K ; ++j) {
                while(ql <= qr && j - Q1[ql] > a[u]) ++ql;
                if(ql <= qr) f2[j] = max(f2[j],Q2[ql] + j * v[u]);
                while(ql <= qr && f1[j] - j * v[u] >= Q2[qr]) --qr;
                Q2[++qr] = f1[j] - j * v[u];Q1[qr] = j;
            }
        }
    }
}
void Init() {
    memset(head,0,sizeof(head));
    memset(lef,0,sizeof(lef));
    sumE = 0;tot = 0;
    memset(f,0,sizeof(int) * Ncnt * (K + 1));
    memset(g,0,sizeof(int) * Ncnt * (K + 1));
    read(N);read(K);
    Ncnt = N;
    for(int i = 1 ; i <= N ; ++i) {
        read(fa[i]);read(a[i]);read(v[i]);
        if(fa[i] != 0) add(fa[i],i);
        lef[fa[i]] = 1;
        tot += a[i];
        if(a[i] > 1) {
            add(i,++Ncnt);fa[Ncnt] = i;v[Ncnt] = v[i];a[Ncnt] = a[i] - 1;a[i] = 1;
        }
    }
    idx = 0;dfsa(1);
    idx = 0;dfsb(1);
}
void Solve() {
    DP(LA,posA,f);
    DP(LB,posB,g);
    int ans = 0;
    for(int u = 1 ; u <= N ; ++u) {
        if(!lef[u]) {
            int *df = f + (K + 1) * (posA[u] - 1),*dg = g + (K + 1) * (posB[u] - siz[u]);
            int64 cur = min((int64)K,tot - dep[u]);
            for(int j = 0 ; j <= cur ; ++j) {
                ans = max(ans,df[j] + dg[cur - j] + sumv[u]);
            }
        }
    }
    out(ans);enter;
}
int main() {
#ifdef ivorysi
    freopen("f1.in","r",stdin);
#endif
    int T;
    read(T);
    while(T--) {
        Init();
        Solve();
    }
    return 0;
}

感覺一看到SDOI R2自動降智
這麽難的省選題要是我去考根本考不動啊

【LOJ】#2268. 「SDOI2017」蘋果樹