1. 程式人生 > >Codeforces Global Round 2 部分題解

Codeforces Global Round 2 部分題解

具體實現 ces mes esp dde 表示 rbegin 什麽 保留

F.Niyaz and Small Degrees

挺sb的一題,為什麽比賽時只過了4個呢

考慮當\(x\)固定的時候怎麽做。顯然可以樹形DP:設\(f_{u,i=0/1}\)表示只考慮\(u\)子樹中的所有點和邊,刪邊使得點\(u\)的度數\(\leq x-i\)且除\(u\)以外的點度數都\(\leq x\)的最小代價。轉移時將\(u\)的所有兒子\(v\)一起考慮,先假設所有\(u,v\)之間的邊都刪掉,把\(f_{u,i}\)加上\(\sum f_{v,0}+w_{u,v}\),再考慮把一些邊加回來。我們可以認為將\(u,v\)之間的邊加回來的代價為\(f_{v,1}-f_{v,0}-w_{u,v}\)

,將所有兒子的代價排序,貪心地取前\(k\)小的邊加回來就可以了。有一種特殊情況是代價為負的兒子個數\(>k\),這種情況下顯然取且僅取所有代價為負的兒子。

現在我們要對所有\(x\in[0,n-1]\)計算答案。可以發現,對於一個固定的\(x\),所有度數\(<=x\)的點一定是合法的。所以我們可以把邊分為三類:兩個端點的度數都\(>x\)的邊(稱為實邊),恰好一個端點的度數\(>x\)的邊(稱為虛邊),兩個端點的度數都\(\leq x\)的邊。

顯然第三類邊對答案沒有影響。對於所有實邊,如果我們只保留原樹中的這些邊,那麽會形成若幹個連通塊。我們可以考慮對於每一個 存在度數\(>x\)

的點 的連通塊做一遍上述DP。這個DP的不同之處在於,每個點會連出若幹條虛邊,我們可能會刪除一些虛邊。這也很好做,只要DP到一個點時枚舉它在實樹上刪掉了多少條與兒子的連邊,就可以計算出至少要刪除多少條與它相連的虛邊,用數據結構維護與每個點相連的所有虛邊,求前\(k\)小即可。

顯然復雜度是\(O(\sum deg*\log n)=O(n\log n)\)的。

具體實現的時候,肯定不能用\(treap\)求前\(k\)小。(你想寫我也是資瓷的)。考慮從小到大枚舉\(x\),隨著\(x\)的遞增,\(k\)是遞減的,所以可以用\(multiset\)維護前\(k\)小。對於一個固定的\(x\),在DP前先枚舉一遍所有度數\(>x\)

的點,將這個點的\(multiset\)刪到\(size()<=k\)。DP到一個點時,暴力將所有它與實樹上的兒子之間的邊加入\(multiset\)並動態維護前\(k\)小,DP完後撤銷這些邊即可。還有一個細節,如果每次DP時都暴力for每條邊並判斷它是否是實邊,復雜度會退化為\(O(n^2)\),所以應該當發現一條邊不再是實邊後就刪除這條邊。

暫時是全CF最短代碼

#include<bits/stdc++.h>
using namespace std;
const int N=250010;
typedef long long ll;

int gi() {
    int x=0,o=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if(ch=='-') o=-1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*o;
}

int to[N<<1],ww[N<<1],nxt[N<<1],tot=1,deg[N],h[N];

void adde(int u,int v,int w) {
    to[++tot]=v,ww[tot]=w,nxt[tot]=h[u],h[u]=tot;
    to[++tot]=u,ww[tot]=w,nxt[tot]=h[v],h[v]=tot;
    ++deg[u],++deg[v];
}

int n,fa[N],x,fw[N],son[N];
ll f[N][2],sum[N];
multiset<ll> s[N];

vector<int> Q[N],P[N];

void dfs(int u) {
    for(int i=h[u];i;i=nxt[i]) {
        int v=to[i];
        if(v!=fa[u]) ++son[u],fa[v]=u,fw[v]=ww[i],dfs(v);
    }
}

void dp(int u) {
    f[u][0]=f[u][1]=0;
    ll w,pre=sum[u];
    vector<ll> ins,del;
    for(int i=h[u],pre=-1;i;pre=i,i=nxt[i]) {
        int v=to[i];
        if(v!=fa[u]&&deg[v]>x) dp(v),f[u][0]+=f[v][1],f[u][1]+=f[v][1],s[u].insert(w=f[v][0]+ww[i]-f[v][1]),ins.push_back(w),sum[u]+=w;
        else if(pre==-1) h[u]=nxt[i];
        else nxt[pre]=nxt[i];
    }
    for(int i=1;~i;i--) {
        int k=son[u]-x+i;
        while(s[u].size()>k) sum[u]-=max(0ll,w=*s[u].rbegin()),s[u].erase(s[u].find(w)),del.push_back(w);
        f[u][i]+=sum[u];
    }
    for(auto w:del) s[u].insert(w);
    for(auto w:ins) s[u].erase(s[u].find(w));
    sum[u]=pre;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
#endif
    n=gi();
    for(int i=1,u,v,w;i<n;i++) u=gi(),v=gi(),w=gi(),adde(u,v,w);
    dfs(1);
    for(int i=2;i<=n;i++) Q[min(deg[i],deg[fa[i]])].push_back(i);
    for(int i=1;i<=n;i++)
        for(int j=0;j<deg[i];j++) P[j].push_back(i);
    for(x=0;x<n;x++) {
        ll w;
        for(auto u:Q[x]) s[fa[u]].insert(fw[u]),sum[fa[u]]+=fw[u];
        for(auto u:P[x]) while(s[u].size()>son[u]-x+1) sum[u]-=max(0ll,w=*s[u].rbegin()),s[u].erase(s[u].find(w));
        ll ans=0;
        for(auto u:P[x]) if(deg[fa[u]]<=x) dp(u),ans+=min(f[u][1],f[u][0]+fw[u]);
        printf("%lld ",ans);
    }
    return 0;
}

G.Get Ready for the Battle

H.Triple

Codeforces Global Round 2 部分題解