• [APIO2021] 封闭道路 T3简要题解


    感觉是整场考试中思维最简单的一题

    考场写了(O(n(sqrt n + log n))),在luogu和uoj都是73,然后可能是因为没打.h,在CCF上爆零了,然后喜提铜牌

    此处做法的复杂度是(O(nlog n))的,在luogu上跑了440ms左右,还是挺快的

    (solution)

    • 首先我们考虑朴素(dp)怎么做,方便起见,以下我们都计算允许每个点保留(k)条边,最大的边权和,容易发现这个和答案刚好是互补的,最后用总数减以下即可

    • 我们注意到每个(k)之间,(dp)是没有关联的,考虑(dp1_u),为(u)的子树内(注意这里不考虑与父亲节点的边是否选择),所有点都选了不超过(k)条边,(u)选了(k)条边的最优方案,(dp2_u)表示(u)的子树内,所有点都选了不超过(k)条边,(u)选了k-1条边的最优方案,这里可以理解为剩下的那条边要连到父亲

    • 考虑如何转移,我们考虑贪心的转移,首先,假设我们一条边都不选,答案显然是(sum_{v in son(u)}{dp1_v}),接下来我们有k次反悔的机会,每次可以选一条边,那么贡献就是(w + dp2_v - dp1_v),我们设它为(f_v),那么(dp1_u = max(sum_{v in son(u)}{dp1_v} + [至多k个f_v])),(dp2_u)则是至多(k-1)(f_v)

    • 容易用排序做到(O(n^2log))

    • 考虑单(log)怎么做,我们不妨先分析点性质

    • 定义:假设我们当前在计算允许保留(k)条边,一个点被定义为无用的,当且仅当(degree(v)(以下简称deg_v) leq k)

    • 首先我们注意到无用点和无用点之间的边我们无需决策,是一定可以选的,这部分贡献我们可以事先用差分来预处理

    for(int u = 0; u < N; ++u)
        for(auto v:E[u]){
            int o = max(deg[u],deg[v]);
            F[o] += w; 
        }
    for(int i = 1; i < N; ++i)  F[i] += F[i-1];
    
    • 考虑有用点(u)和无用点(v)之间的决策,我们发现这部分决策是平凡的,因为如果一个点成为了无用点,那么在之后,其贡献将一直不变,保持为(w(u,v)),那么我们可以考虑用一个堆来维护,那么我们每次可以只计算有用点和有用点之间的贡献,不够的再直接用无用点的堆的前缀和来补充
    • 此部分代码如下(注意此处无需考虑父子关系)
    void _delete(int u){
    	for(auto now:E[u]){
    		int v = now.fi,w = now.se;
    		q[v].push(-w);Sw[v] += w;
    	}
    }
    
    • 那么我们可以发现,对于每个(k),我们只需保留(deg ge k)的点形成的森林来遍历,我们可以先在邻接表上按度数排序,用vector的pop_back和双指针来删除点,这样做的复杂度看起来很暴力,但是实际上我们可以分析,一个点至多只会被遍历度数次,而所有的点的度数和是线性级别的,加上堆和排序的复杂度,总复杂度就是(O(n log n))
    • 有一些细节,比如如何计算堆的前(k)大的和?如果嫌麻烦,可以直接暴力权值线段树,反正空间1G,假设当前计算到(k),我们可以让堆只保留前(k)大的,剩下的先弹进另一个堆里(这里可以叫做垃圾堆),待更新到(k+1)时,再尝试从垃圾堆中拿出最大的来更新堆,这样的话,对于每个点,都有堆大小(leq)度数,而根据我们之前的分析,总度数和是线性,那么加上堆的复杂度就是单log的
    • 时间复杂度(O(nlog n)),空间复杂度(O(n))
    • 代码如下
    #include<bits/stdc++.h>
    std::vector<long long> minimum_closure_costs(int N, std::vector<int> U,
                                                 std::vector<int> V,
                                                 std::vector<int> W);
    using namespace std;
    int read(){
    	char c = getchar();
    	int x = 0;
    	while(c < '0' || c > '9')	c = getchar();
    	while(c >= '0' && c <= '9')	x = x * 10 + c - 48,c = getchar();
    	return x;
    }
    const int _ = 1e5 + 7;
    int deg[_];int n;
    #define ll long long
    #define mp make_pair
    #define fi first
    #define se second
    #define pb push_back 
    typedef pair<int,int> pii;
    vector<pii>E[_];
    vector<ll>F;
    vector<int>Ver;
    bool cmp(pii a,pii b){
    	return deg[a.fi] > deg[b.fi];
    }
    bool Cmp(int a,int b){
    	return deg[a] < deg[b];
    }
    priority_queue<ll>q[_],bq[_];ll Sw[_];
    int dfn[_],dfncnt;
    void dfs1(int u,int fa){
    	for(auto now:E[u]){
    		int v = now.fi,w = now.se;
    		if(v ^ fa){
    			int o = max(deg[u],deg[v]);
    			F[o] += w;
    //			cout<<o<<" "<<w<<'
    '; 
    			dfs1(v,u); 
    		}
    	}
    }
    ll dp1[_],dp2[_];
    int st[_],top,bac[_],bcnt;
    void _delete(int u){
    	for(auto now:E[u]){
    		int v = now.fi,w = now.se;
    		q[v].push(-w);Sw[v] += w;
    	}
    }
    void work(int u,int par,const int D){
    	dfn[u] = D;
    	dp1[u] = dp2[u] = 0;/*dp1[u]取D个,dp2[u]取D-1个*/
    	for(auto now:E[u]){
    		int v = now.fi;
    		if(v ^ par){
    //			cout<<u<<' '<<v<<"E"<<'
    ';
    			work(v,u,D);
    		}
    	}
    	ll ans = 0;
    	top = 0;
    	for(auto now:E[u]){
    		int v = now.fi,w = now.se;
    		if(v ^ par){
    			ans += dp1[v];
    			if(w > (dp1[v] - dp2[v])){
    				st[++top] = w - (dp1[v] - dp2[v]);
    			}
    		}
    	}
    	sort(st + 1,st + top + 1);
    	reverse(st + 1,st + top + 1);
    	ll S1 = 0,S2 = Sw[u];
    	dp1[u] = Sw[u];dp2[u] = Sw[u];
    //	if(D == 2)	cout<<Sw[u]<<'
    ';
    	assert(q[u].size() <= (unsigned int)D);
    	if((int)q[u].size() == D)	dp2[u] += q[u].top();
    //	cout<<u<<'
    ';
    	bcnt = 0;
    //	cout<<u<<'
    ';
    	for(int i = 1; i <= min(top,D); ++i){/*选i个有用点,D-i个无用点*/
    		S1 += st[i];
    		if((int)q[u].size() > D - i){
    			bac[++bcnt] = q[u].top();
    			S2 += bac[bcnt];
    			q[u].pop();
    		}
    		dp1[u] = max(dp1[u],S2 + S1);
    		if(i == D)		continue;
    		ll V = S2 + S1;if((int)q[u].size() >= D - i){
    			V += q[u].top(); 
    		}
    		dp2[u] = max(dp2[u],V);
    	}
    //	cout<<u<<'
    ';
    	while(bcnt)	q[u].push(bac[bcnt]),bcnt--;
    //	if(D == 2 && u == 1)	cout<<dp1[u]<<'
    ';
    	dp1[u] += ans;dp2[u] += ans;
    	if(!par)	F[D] += dp1[u]; 
    }
    //vector<int> U;vector<int> V;vector<int> W;
    vector<ll> minimum_closure_costs(int N,vector<int> U,vector<int> V,vector<int> W) {
    	ll S = 0;
    //	int N = read();
    	n = N;
    	/*for(int i = 0; i < N - 1; ++i){
    		U.pb(0),V.pb(0),W.pb(0);
    		U[i] = read(),V[i] = read(),W[i] = read();
    	}*/
    	for(int i = 1; i < N; ++i){
    		int u = U[i-1] + 1,v = V[i-1] + 1,w = W[i-1];
    //		u--,v--;
    		deg[u]++,deg[v]++;
    		S += w;
    		E[u].pb(mp(v,w));E[v].pb(mp(u,w));
    	}
    	for(int i = 1; i <= N; ++i)	F.pb(0);
    	for(int i = 1; i <= N; ++i)	Ver.pb(i);
    	sort(Ver.begin(),Ver.end(),Cmp);Ver.pb(0);
    	dfs1(1,0);
    	for(int i = 1; i <= N; ++i)	sort(E[i].begin(),E[i].end(),cmp);
    	for(int i = 1; i < N; ++i)	F[i] += F[i-1];
    	int l = 0;
    //	for(int i = 0; i < N; ++i)	cout<<Ver[i]<<' ';
    //	cout<<'
    ';
    	for(int i = 1; i < N; ++i){
    		while(l <= N && deg[Ver[l]] <= i){
    			_delete(Ver[l]);
    			l++;
    		}
    //		cout<<i<<' '<<l<<'
    ';
    		for(int ver = l; ver < N; ++ver){
    			int u = Ver[ver];
    			int k = E[u].size() - 1;
    			while(k >= 0 && deg[E[u][k].fi] <= i){
    				E[u].pop_back();
    				k--;
    			}
    		}
    //		puts("hxsb");
    		for(int ver = l; ver < N; ++ver){
    			int u = Ver[ver];
    			while((int)q[u].size() > i){
    				ll x = -q[u].top();
    				q[u].pop();Sw[u] -= x;
    				bq[u].push(x);
    			}
    			if((int)q[u].size() < i){
    				if((int)bq[u].size()){
    					ll x = bq[u].top();
    					bq[u].pop();q[u].push(-x);Sw[u] += x;
    				}
    			}
    			else{
    				if((int)bq[u].size()) {
    					ll x = bq[u].top();ll y = -q[u].top();
    					if(x > y){
    						q[u].pop();bq[u].pop();
    						Sw[u] -= y;Sw[u] += x;
    						q[u].push(-x);bq[u].push(y);
    					}
    				}
    			}
    		} 
    		for(int ver = l; ver < N; ++ver){
    			int u = Ver[ver];
    			if(dfn[u] != i)	work(u,0,i);
    		}
    	}
    	for(int i = 0; i < N; ++i)	F[i] = S - F[i];
    //	for(int i = 0; i < N; ++i)	cout<<F[i]<<' ';
    	return F;
    }
    /*int main(){
    	freopen("data.in","r",stdin);
    	freopen("data.out","w",stdout);
    	minimum_closure_costs();
    	return 0;
    }*/
    
  • 相关阅读:
    韦到头打印链表
    字符串替换
    二维数组中的查找
    字符串加解密
    简单密码破解
    vue中store模块化
    使用requests抓取网页内容
    PyQt中的一些小问题
    微信公众号之Token验证
    编译BOOST中的PYTHON模块
  • 原文地址:https://www.cnblogs.com/y-dove/p/14824013.html
Copyright © 2020-2023  润新知