【Codeforces671D】Roads in Yusland

题目链接

中文翻译版题目

ps:两个略有不同,原题数据范围是n<=3e5,且数据保证输入的b是a的祖先。


算法:ccz 大爷自创,大概就是并查集+启发式合并+平衡树维护。

分析:

先dfs预处理出每个点的深度,然后对于每个点都建立一棵平衡树来储存以这个点为起点的路径,按照第一关键字终点的深度从小到大第二关键字代价从小到大来建立(第二关键字只有在第一关键字相等的时候起作用,因为最后不会有深度相等的点,所以不会影响最后代价的排序),平衡树可以用STL里的multiset(可重集)。

每次往平衡树里加点的时候,查询这个点的前驱,若前驱代价反而小于或等于当前点的代价,说明其前驱必然比当前点(即当前路径)要优,删去该点。

如果这个点没有被删除,就循环处理它的后继,若其后继代价反而比当前点大,说明其后继必然比当前点差,删去其后继。

(ps:这里尤其要注意好特判是否为begin或end)

通过每次插入都进行以上处理,就能维护出一棵终点深度从小到大,代价从大到小的平衡树。(也就是说越长的路径代价越大)

接着用dfs后序处理原树(每次处理到的点必为叶子节点,当访问完一个节点的子节点时,此节点将成为新的叶子节点,所以根节点1必然为最后一个访问到的),首先删去该树中已经退化成一个点的路径(即点的tp等于当前节点的深度,或者说起点就是终点)。然后取出该平衡树最后一个节点(即代价最小的路径),选取这条路径并加上答案,并且将其值作为当前平衡树的标记(在启发式合并的时候要用到)。这棵树内的其他路径都要减去这条路径的代价,最后把这条路径所覆盖到的点都放入同一个集合中再删除这条路径,把这棵平衡树接种进它父亲的那棵平衡树中(就是把以这个节点为起点的路径起点改为其父亲)。

(ps:如果在取这棵树代价最小的点时发现该树已经为空且该节点不为根节点,说明找不到可以覆盖从根节点到此节点的边的路径,即无解)

其实就是一种类似于网络流的具有后效性的贪心......

代码:

 1 #include<cstdio>
 2 #include<set>
 3 #include<algorithm>
 4 #include<vector>
 5 typedef long long LL;
 6 const int maxn=3e5+5;
 7 struct point{
 8     int next,to;
 9 }e[maxn*2];
10 struct node{
11     int tp,v;
12     bool operator<(const node&p)const{return p.tp>tp||(p.tp==tp&&p.v>v);}
13 };
14 int n,m,tot=0;
15 int fa[maxn],deep[maxn],first[maxn],dec[maxn],ff[maxn];
16 LL an=0;
17 typedef std::multiset<node> me;
18 typedef me::iterator IT;
19 me f[maxn];
20 int getf(int x){
21     if(x==ff[x])return x;
22     ff[x]=getf(ff[x]);
23     return ff[x];
24 }
25 void ins(int ai,node p,int d){
26     p.v+=d;
27     IT it=f[ai].insert(p),it1=f[ai].begin();
28     IT it0=it;
29     if(it0!=it1&&(--it0)->v<=p.v){
30         f[ai].erase(it);return;
31     }it0=it;it++;
32     for(;it!=f[ai].end()&&it->v>=p.v;){
33         it0=it;
34         it++;
35         f[ai].erase(it0);
36     }
37 }
38 void add(int u,int v){
39     tot++;e[tot].next=first[u];first[u]=tot;e[tot].to=v;
40     tot++;e[tot].next=first[v];first[v]=tot;e[tot].to=u;
41 }
42 void dfs1(int x,int ff){
43     for(int i=first[x];i;i=e[i].next){
44         int to=e[i].to;
45         if(to==ff)continue;
46         fa[to]=x;
47         deep[to]=deep[x]+1;
48         dfs1(to,x);
49     }
50 }
51 void dfs2(int x){
52     for(int i=first[x];i;i=e[i].next){
53         int to=e[i].to;
54         if(to!=fa[x]){
55             dfs2(to);
56             if(f[to].size()>f[x].size())f[x].swap(f[to]),std::swap(dec[x],dec[to]);
57             for(IT it=f[to].begin();it!=f[to].end();++it)ins(x,*it,dec[x]-dec[to]);
58             f[to].clear();
59         }
60     }
61     while(!f[x].empty()){
62         IT it=--f[x].end();
63         if(it->tp==deep[x]){f[x].erase(it);continue;}
64         break;
65     }
66     if(x==ff[x]){
67         if(x==1)return;
68         if(f[x].empty())printf("-1"),exit(0);
69         IT it=--f[x].end();
70         an+=it->v-dec[x];
71         dec[x]=it->v;int x0=x;
72         for(;deep[x0]>it->tp;x0=ff[x0])ff[x0]=getf(fa[x0]);
73         f[x].erase(it);
74     }
75 }
76 int main(){
77     scanf("%d %d",&n,&m);
78     for(int i=1;i<=n;i++)ff[i]=i;
79     for(int i=1,a,b;i<=n-1;i++){
80         scanf("%d %d",&a,&b);
81         add(a,b);
82     }
83     deep[1]=1;dfs1(1,0);
84     for(int i=1,a,b,c;i<=m;i++){
85         scanf("%d %d %d",&a,&b,&c);
86         ins(a,(node){deep[b],c},0);
87     }
88     dfs2(1);
89     printf("%lld",an);
90     return 0;
91 }
Codeforces 671D
原文地址:https://www.cnblogs.com/JKAI/p/7481665.html