[luogu7600]封闭道路
作者:互联网
对于确定的$K$,问题也可以看作每一个点最多选$K$条出边,并最大化选择的边权和
关于这个问题,有如下的树形dp——
令$f_{k,0/1}$表示以$k$为根的子树中,根节点选择不超过$K/K-1$个儿子的最大边权和,转移为
$$
\begin{cases}f_{k,0}=\sum_{x\in son_{k}}f_{x,0}+\max_{S\subseteq son_{k},|S|\le K}\sum_{x\in S}(f_{x,1}+val_{k,x}-f_{x,0})\\f_{k,1}=\sum_{x\in son_{k}}f_{x,0}+\max_{S\subseteq son_{k},|S|<K}\sum_{x\in S}(f_{x,1}+val_{k,x}-f_{x,0})\end{cases}
$$
(其中$son_{k}$为$k$儿子的集合,$val_{x,y}$表示边$(x,y)$的边权)
对于后者,我们可以维护一个数据结构,支持加入一个元素和查询最大的$K$(或$K-1$)个元素之和
可以使用平衡树/权值线段树实现,单次复杂度为$o(\log n)$,总复杂度为$o(n\log n)$
记$deg_{k}$为节点$k$的度数,将所有边$(x,y)$分为三类:
1.$deg_{x},deg_{y}\le K$,这一类边一定可以选择,直接将$val_{(x,y)}$加入答案并删除
2.$deg_{x}\le K<deg_{y}$,这一类边实际上仅在$y$上有限制,我们可以在求$f_{y,0/1}$时的平衡树中先加入此边权即可
另外,为了维护,在dp结束后要删除$f_{x,1}+val_{k,x}-f_{x,0}$,如果直接在multiset中删除会导致节点个数
3.$deg_{x},deg_{y}>K$,这一类边用之前的dp即可
由此,每一次dp的节点数只有$deg_{k}>K$的节点,而$\sum_{K=0}^{n-1}\sum_{deg_{k}> K}1=o(n)$,总复杂度即$o(n\log n)$
(另外注意搜索时,$deg_{k}>K$的点的出边中要删除$deg_{k}\le K$的点,来保证复杂度)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 100005 4 #define ll long long 5 #define pii pair<int,int> 6 #define mid (l+r>>1) 7 struct Edge{ 8 int nex,to,len; 9 }edge[N<<1]; 10 vector<pii>G[N]; 11 vector<ll>ans,v[N]; 12 int E,V,n,tmp,r[N],id[N],head[N],work[N],vis[N],rt[N],ls[N*100],rs[N*100],sz[N*100]; 13 ll sum,f[N*100],dp[N][2]; 14 bool cmp1(int x,int y){ 15 return r[x]<r[y]; 16 } 17 bool cmp2(pii x,pii y){ 18 return r[x.first]>r[y.first]; 19 } 20 void update(int &k,int l,int r,int x,int y){ 21 if (!k)k=++V; 22 sz[k]+=y,f[k]+=x*y; 23 if (l==r)return; 24 if (x<=mid)update(ls[k],l,mid,x,y); 25 else update(rs[k],mid+1,r,x,y); 26 } 27 ll query(int k,int l,int r,int x){ 28 if (l==r)return 1LL*min(x,sz[k])*l; 29 if (sz[rs[k]]>=x)return query(rs[k],mid+1,r,x); 30 return f[rs[k]]+query(ls[k],l,mid,x-sz[rs[k]]); 31 } 32 void add(int x,int y,int z){ 33 edge[E].nex=head[x]; 34 edge[E].to=y; 35 edge[E].len=z; 36 head[x]=E++; 37 } 38 void dfs(int k){ 39 vis[k]=1; 40 dp[k][0]=dp[k][1]=0; 41 for(int &i=work[k];i!=-1;i=edge[i].nex) 42 if (r[edge[i].to]>tmp)break; 43 for(int i=work[k];i!=-1;i=edge[i].nex) 44 if (!vis[edge[i].to]){ 45 dfs(edge[i].to); 46 dp[k][0]+=dp[edge[i].to][0]; 47 ll s=dp[edge[i].to][1]+edge[i].len-dp[edge[i].to][0]; 48 if (s>0){ 49 update(rt[k],1,1e9,s,1); 50 v[k].push_back(s); 51 } 52 } 53 dp[k][1]=dp[k][0]; 54 dp[k][0]+=query(rt[k],1,1e9,tmp); 55 dp[k][1]+=query(rt[k],1,1e9,tmp-1); 56 for(int i=0;i<v[k].size();i++)update(rt[k],1,1e9,v[k][i],-1); 57 v[k].clear(); 58 } 59 vector<ll> minimum_closure_costs(int n,vector<int>u,vector<int>v,vector<int>w){ 60 memset(head,-1,sizeof(head)); 61 for(int i=0;i<=n-2;i++){ 62 u[i]++,v[i]++; 63 G[u[i]].push_back(make_pair(v[i],w[i])); 64 G[v[i]].push_back(make_pair(u[i],w[i])); 65 sum+=w[i]; 66 } 67 for(int i=1;i<=n;i++){ 68 id[i]=i; 69 r[i]=G[i].size(); 70 } 71 sort(id+1,id+n+1,cmp1); 72 for(int i=1;i<=n;i++){ 73 sort(G[i].begin(),G[i].end(),cmp2); 74 for(int j=0;j<G[i].size();j++)add(i,G[i][j].first,G[i][j].second); 75 } 76 memcpy(work,head,sizeof(work)); 77 ans.push_back(sum); 78 for(int i=1,j=1;i<n;i++){ 79 while ((j<=n)&&(r[id[j]]<=i)){ 80 for(int k=head[id[j]];k!=-1;k=edge[k].nex){ 81 if (vis[edge[k].to])sum-=edge[k].len; 82 else update(rt[edge[k].to],1,1e9,edge[k].len,1); 83 } 84 vis[id[j++]]=1; 85 } 86 tmp=i; 87 ans.push_back(sum); 88 for(int k=j;k<=n;k++) 89 if (!vis[id[k]]){ 90 dfs(id[k]); 91 ans[i]-=dp[id[k]][0]; 92 } 93 for(int k=j;k<=n;k++)vis[id[k]]=0; 94 } 95 return ans; 96 }View Code
标签:int,sum,封闭,luogu7600,son,edge,道路,dp,deg 来源: https://www.cnblogs.com/PYWBKTDA/p/14970654.html