Codeforces Round #423 (Div. 1, rated, based on VK Cup Finals) D. Best Edge Weight(最小生成树+LCA+树链剖分)

来源:互联网 发布:浙江省纺织品出口数据 编辑:程序博客网 时间:2024/06/09 14:38

题目链接:http://codeforces.com/contest/827/problem/D


数据结构,很休闲的。。。


首先我们要生成一颗最小生成树,然后,对于树上的边,答案是树外边中的最小值-1,其中树外边指的是在最小生成树过程中,可以替代当前边的所有边,然后对于每一个不在最小生成树上的边(u,v),答案是树上从u到v路径的最大值-1


然后我们对这两种情况分开维护,对于树上路径,我们用LCA可以在logn时间内求出某个路径的最大值,对于树外的每条边(u,v),他对所有在u到v路径上的边都有贡献,这就需要支持修改树上路径的操作,我们用树链剖分维护,单词操作复杂度也是logn,总体在O(nlogn)内就可以解决


貌似不用这么麻烦???


代码:

#include<bits/stdc++.h>using namespace std;const int INF=0x3f3f3f3f;bool book[200010];struct E{int u,v,w;}sv[200010];namespace LCA{const int MAXN=200010; const int DEG=20;struct Edge {int to,next,w;}edge[MAXN*2]; int head[MAXN],tot; void addedge(int u,int v,int w){edge[tot].to=v;edge[tot].w=w;edge[tot].next=head[u];head[u]=tot++;}void init(){tot=0;memset(head,-1,sizeof(head)); }int fa[MAXN][DEG],mx[MAXN][DEG];//fa[i][j]表示结点i的第2^j个祖先 int deg[MAXN];//深度数组void BFS(int root){queue<int>que;deg[root]=0;fa[root][0]=root;mx[root][0]=0;que.push(root);while(!que.empty()){int tmp=que.front();que.pop();for(int i=1;i<DEG;i++){fa[tmp][i]=fa[fa[tmp][i-1]][i-1];mx[tmp][i]=max(mx[fa[tmp][i-1]][i-1],mx[tmp][i-1]);}for(int i=head[tmp];i!=-1;i=edge[i].next) {int v=edge[i].to;if(v==fa[tmp][0])continue;deg[v]=deg[tmp]+1;fa[v][0]=tmp;mx[v][0]=edge[i].w;que.push(v);}}}int LCA(int u,int v){if(deg[u]>deg[v])swap(u,v);int ret=0;int hu=deg[u],hv=deg[v];int tu=u,tv=v;for(int det=hv-hu,i=0;det;det>>=1,i++)if(det&1){ret=max(mx[tv][i],ret);tv=fa[tv][i];}if(tu==tv)return ret;for(int i=DEG-1;i>=0;i--){if(fa[tu][i]==fa[tv][i])continue;ret=max(ret,mx[tu][i]);ret=max(ret,mx[tv][i]);tu=fa[tu][i];tv=fa[tv][i];}return max(ret,max(mx[tu][0],mx[tv][0]));}}namespace Kruskal{const int MAXN=2e5+5;//最大点数 const int MAXM=2e5+5;//最大边数 int F[MAXN];//并查集使用 struct Edge {int u,v,w,id;}edge[MAXM];//存储边的信息,包括起点/终点/权值 int tol;//边数,加边前赋值为0 void init()//初始化 {tol=0;memset(F,-1,sizeof(F));memset(edge,0,sizeof(edge));}void addedge(int u,int v,int w,int id){edge[tol].u=u;edge[tol].v=v;edge[tol].id=id;edge[tol++].w=w;}bool cmp(Edge a,Edge b)//排序函数,讲边按照权值从小到大排序 {return a.w<b.w; }int find(int x)//并查集使用 {if(F[x]==-1)return x;else return F[x]=find(F[x]);}}namespace tree{const int MAXN=200010;#define lson l,mid,rt<<1#define rson mid+1,r,rt<<1|1struct Edge{int to,next;}edge[MAXN*2]; int head[MAXN],tot; int top[MAXN];//top[v]表示v所在的重链的顶端节点 int fa[MAXN]; //父亲节点int deep[MAXN];//深度int num[MAXN];//num[v]表示以v为根的子树的节点数int p[MAXN];//p[v]表示v与其父亲节点的连边在线段树中的位置int fp[MAXN];//和p数组相反int son[MAXN];//重儿子int pos; void init(){tot=0;memset(head,-1,sizeof(head));pos=0;memset(son,-1,sizeof(son));}void addedge(int u,int v){edge[tot].to=v;edge[tot].next=head[u];head[u]=tot++;}void dfs1(int u,int pre,int d) //第一遍dfs求出fa,deep,num,son {deep[u]=d;fa[u]=pre;num[u]=1;for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].to;if(v!=pre){dfs1(v,u,d+1);num[u]+=num[v];if(son[u]==-1||num[v]>num[son[u]])son[u]=v;}}}void getpos(int u,int sp) //第二遍dfs求出top和p{top[u]=sp;p[u]=pos++;fp[p[u]]=u;if(son[u]==-1)return;getpos(son[u],sp);for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].to;if(v!=son[u]&&v!=fa[u])getpos(v,v);}}//线段树 struct Node{int Min,lazy;}segTree[MAXN*4];void build(int i,int l,int r){segTree[i].Min=INF;segTree[i].lazy=0;if(l==r)return;int mid=(l+r)>>1;build(i<<1,l,mid);build((i<<1)|1,mid+1,r);}void push_up(int i) {segTree[i].Min=max(segTree[i<<1].Min,segTree[(i<<1)|1].Min);}void push_down(int i){if(segTree[i].lazy){segTree[i<<1].Min=min(segTree[i<<1].Min,segTree[i].lazy);segTree[i<<1|1].Min=min(segTree[i<<1|1].Min,segTree[i].lazy);if(segTree[i<<1].lazy==0)segTree[i<<1].lazy=segTree[i].lazy;elsesegTree[i<<1].lazy=min(segTree[i<<1].lazy,segTree[i].lazy);if(segTree[i<<1|1].lazy==0)segTree[i<<1|1].lazy=segTree[i].lazy;elsesegTree[i<<1|1].lazy=min(segTree[i<<1|1].lazy,segTree[i].lazy);segTree[i].lazy=0;}}void update(int L,int R,int val,int l,int r,int rt){if(L<=l&&r<=R){segTree[rt].Min=min(segTree[rt].Min,val);if(segTree[rt].lazy==0)segTree[rt].lazy=val;elsesegTree[rt].lazy=min(segTree[rt].lazy,val);return;}push_down(rt);int mid=(l+r)>>1;if(L<=mid)update(L,R,val,lson);if(mid<R)update(L,R,val,rson);push_up(rt);}int query(int pos,int l,int r,int rt){if(l==r)return segTree[rt].Min;int mid=(l+r)>>1;push_down(rt);if(pos<=mid)return query(pos,lson);if(mid<pos)return query(pos,rson);}void upd(int u,int v,int val){int f1=top[u],f2=top[v];while(f1!=f2){if(deep[f1]<deep[f2]){swap(f1,f2);swap(u,v);}update(p[f1],p[u],val,0,pos-1,1);u=fa[f1];f1=top[u];}if(u==v)return ;if(deep[u]>deep[v]) swap(u,v);update(p[son[u]],p[v],val,0,pos-1,1);}int find(int u,int v)//查询u->v边的最大值 {if(deep[u]>deep[v]) swap(u,v);return query(p[v],0,pos-1,1);}}void Kru(int n)//传入点数,返回最小生成树的权值,如果不连通返回-1 {using namespace Kruskal;memset(F,-1,sizeof(F));sort(edge,edge+tol,cmp);int cnt=0;//计算加入的边数 for(int i=0;i<tol;i++){ int u=edge[i].u;int v=edge[i].v;int w=edge[i].w;int id=edge[i].id;int t1=find(u);int t2=find(v);if(t1!=t2){F[t1]=t2;book[id]=true;cnt++;tree :: addedge(u,v);tree :: addedge(v,u);LCA :: addedge(u,v,w);LCA :: addedge(v,u,w);}if(cnt==n-1)break;}}int main(){//freopen("in.txt","r",stdin);//freopen("out.txt","w",stdout);tree :: init();LCA :: init();Kruskal :: init();int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){scanf("%d%d%d",&sv[i].u,&sv[i].v,&sv[i].w);Kruskal :: addedge(sv[i].u,sv[i].v,sv[i].w,i);}Kru(n);LCA :: BFS(1);tree :: dfs1(1,0,0);tree :: getpos(1,0);tree :: build(1,0,tree :: pos-1);for(int i=1;i<=m;i++){int u=sv[i].u,v=sv[i].v,w=sv[i].w;if(!book[i])tree :: upd(u,v,w);}for(int i=1;i<=m;i++){int u=sv[i].u,v=sv[i].v;if(!book[i]){int ret=LCA :: LCA(u,v);printf("%d ",ret-1);}else{int ret=tree :: find(u,v);if(ret==INF)printf("-1 ");elseprintf("%d ",ret-1);}}printf("\n");return 0;}


阅读全文
0 0
原创粉丝点击