树剖+线段树||树链剖分||BZOJ2238||Mst

本文介绍了一种解决特定图论问题的算法,通过构建最小生成树并使用树剖技术,有效地处理了非树边的路径最小值计算。文章详细阐述了Kruskal算法的应用过程,以及如何维护和更新线段树来快速查询和修改路径信息。
摘要由CSDN通过智能技术生成

题面:https://www.lydsy.com/JudgeOnline/problem.php?id=2238

思路:先求个最小生成树,然后就对最小生成树上的边做树剖,依次对非树边进行处理,维护非树边两端连成的路径的最小值(用非树边的权值维护),然后对于每个询问,求出覆盖在那条线段上的最小值,用real_sum(最小生成树的边权和)去加加减减就行了。

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<cstdlib>
  6 #define min(a,b) ((a)<(b)?(a):(b))
  7 using namespace std;
  8 const int maxn=50005,maxm=100005,maxw=10005,inf=1<<30;
  9 int N,M,x,y,w,Q,edge_head[maxn],num_edge=0,edge_head2[maxn],num_edge2=0,fa[maxn],real_sum=0;
 10 int size[maxn],son[maxn],seg[maxn],rev[maxn],f[maxn],dep[maxn],top[maxn],X,Y,_ans,T;
 11 bool via[maxm],ans=1;
 12 inline int rd(){
 13     int x=0,f=1;char c=getchar();
 14     while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
 15     while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
 16     return f*x;
 17 }
 18 struct Edge{
 19     int to,dis,nx,from;
 20 }edge[maxm<<1];
 21 struct Edge2{
 22     int to,dis,nx,from,id;
 23 }edge2[maxm];
 24 inline void Add_edge(int from,int to,int dis){
 25     edge[++num_edge].nx=edge_head[from];
 26     edge[num_edge].from=from;
 27     edge[num_edge].to=to;
 28     edge[num_edge].dis=dis;
 29     edge_head[from]=num_edge;
 30     return;
 31 }
 32 inline void Add_edge2(int from,int to,int dis){
 33     edge2[++num_edge2].nx=edge_head2[from];
 34     edge2[num_edge2].from=from;
 35     edge2[num_edge2].to=to;
 36     edge2[num_edge2].dis=dis;
 37     edge2[num_edge2].id=num_edge2;
 38     edge_head2[from]=num_edge2;
 39     return;
 40 }
 41 inline bool cmp(const Edge2&a,const Edge2&b){
 42     if(a.dis<b.dis)return 1;
 43     return 0;
 44 }
 45 inline int getf(int x){
 46     if(fa[x]==x)return x;
 47     fa[x]=getf(fa[x]);
 48     return fa[x];
 49 }
 50 void Kruskal(){
 51     sort(edge2+1,edge2+num_edge2+1,cmp);
 52     for(int i=1;i<=N;i++)fa[i]=i;
 53     int num=0;
 54     for(int i=1;i<=num_edge2;i++){
 55         int x=edge2[i].from,y=edge2[i].to;
 56         int fx=getf(x),fy=getf(y);
 57         if(fx!=fy){
 58             num++;
 59             fa[fx]=fy;
 60             via[edge2[i].id]=1;
 61             real_sum+=edge2[i].dis;
 62             if(num==N-1)break;
 63         }
 64     }
 65     if(num!=N-1)ans=0;
 66     return;
 67 }
 68 inline void Dfs1(int x,int _f){
 69     f[x]=_f;
 70     size[x]=1;
 71     dep[x]=dep[_f]+1;
 72     for(int i=edge_head[x];i;i=edge[i].nx){
 73         int y=edge[i].to;
 74         if(y!=_f&&((((i&1)==1)&&via[(i>>1)+1])||(((i&1)==0)&&via[i>>1]))){
 75             Dfs1(y,x);
 76             size[x]+=size[y];
 77             if(size[y]>size[son[x]])son[x]=y;
 78         }
 79     }
 80     return;
 81 }
 82 inline void Dfs2(int x){
 83     if(son[x]){
 84         int y=son[x];
 85         top[y]=top[x];
 86         seg[y]=++seg[0];
 87         rev[seg[0]]=y;
 88         Dfs2(y);
 89     }
 90     for(int i=edge_head[x];i;i=edge[i].nx){
 91         int y=edge[i].to;
 92         if(top[y]==0&&((((i&1)==1)&&via[(i>>1)+1])||(((i&1)==0)&&via[i>>1]))){
 93             top[y]=y;
 94             seg[y]=++seg[0];
 95             rev[seg[0]]=y;
 96             Dfs2(y);
 97         }
 98     }
 99     return;
100 }
101 struct Tree{
102     int l,r,mina,lazy;
103 }t[maxn<<3];
104 inline void Build(int x,int l,int r){
105     t[x].l=l;t[x].r=r;t[x].mina=inf;t[x].lazy=inf;
106     if(l==r)return;
107     int ls=x<<1,rs=x<<1|1,mid=(l+r)>>1;
108     Build(ls,l,mid);Build(rs,mid+1,r);
109     return;
110 }
111 inline void Pushdown(int x){
112     int ls=x<<1,rs=x<<1|1,lazy=t[x].lazy;
113     if(lazy!=inf){
114         t[ls].mina=min(t[ls].mina,lazy);t[rs].mina=min(t[rs].mina,lazy);
115         t[ls].lazy=min(t[ls].lazy,lazy);t[rs].lazy=min(t[rs].lazy,lazy);
116         t[x].lazy=inf;
117     }
118     return;
119 }
120 inline void Update(int x,int ql,int qr,int e){
121     int l=t[x].l,r=t[x].r;
122     if(ql<=l&&r<=qr){
123         t[x].mina=min(t[x].mina,e);
124         t[x].lazy=min(t[x].lazy,e);
125         return;
126     }
127     int ls=x<<1,rs=x<<1|1,mid=(l+r)>>1;
128     Pushdown(x);
129     if(ql<=mid)Update(ls,ql,qr,e);
130     if(qr>mid)Update(rs,ql,qr,e);
131     return;
132 }
133 inline void Query(int x,int q){
134     int l=t[x].l,r=t[x].r;
135     if(q==l&&l==r){
136         _ans=min(_ans,t[x].mina);
137         return;
138     }
139     int mid=(l+r)>>1,ls=x<<1,rs=x<<1|1;
140     Pushdown(x);
141     if(q<=mid)Query(ls,q);
142     else Query(rs,q);
143     return;
144 }
145 inline void Work(int x,int y,int dis){
146     int fx=top[x],fy=top[y];
147     while(fx!=fy){
148         if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
149         Update(1,seg[fx],seg[x],dis);
150         x=f[fx];fx=top[x];
151     }
152     if(dep[x]>dep[y])swap(x,y);
153     if(x==y)return;
154     Update(1,seg[x]+1,seg[y],dis);
155     return;
156 }
157 int main(){
158     N=rd();M=rd();
159     for(int i=1;i<=M;i++){
160         x=rd();y=rd();w=rd();
161         Add_edge2(x,y,w);
162         Add_edge(x,y,w);
163         Add_edge(y,x,w);
164     }
165     Kruskal();
166     scanf("%d",&Q);
167     Dfs1(1,0);
168     seg[0]=seg[1]=rev[1]=top[1]=1;
169     Dfs2(1);
170     Build(1,1,seg[0]);
171     for(int i=1;i<=M;i++)if(via[i]==0)Work(edge[i<<1].from,edge[i<<1].to,edge[i<<1].dis);
172     while(Q--){
173         scanf("%d",&T);
174         if(ans==0){
175             puts("Not connected");
176             continue;
177         }
178         if(via[T]==0)printf("%d\n",real_sum);
179         else{
180             X=edge[T<<1].from,Y=edge[T<<1].to;_ans=inf;
181             if(dep[X]>dep[Y])Query(1,seg[X]);else Query(1,seg[Y]);
182             if(_ans==inf){
183                 puts("Not connected");
184                 continue;
185             }
186             _ans=real_sum-edge[T<<1].dis+_ans;
187             printf("%d\n",_ans);
188         }
189     }
190     return 0;
191 }

后记:“&”的优先级小于“==”的优先级;线段树查询时要注意ql不能大于qr;边权下移后处理点权时要小心;什么情况要pushdown、pushup要考虑清楚;我的线段树真菜

转载于:https://www.cnblogs.com/AlenaNuna/p/10356782.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值