NOIP2015运输计划的一个O(n)解法

偶尔善良 提交于 2019-11-27 10:22:54

一个O(n)的解法。

不难发现有如下性质:

  • 被改造成虫洞的边一定在最长路径上

那么,我们用类似提直径的方法把这条路径给拎出来

就会形成这样的一棵树。

那么,对于一条边,若其被改成虫洞,最长路径会有如下三种情况:

  • 依然是当前的最长路径

  • 被改造边左端点的最长路径

  • 被改造边右端点的最长路径

难道我们不用考虑经过该边的路径吗??

当然不用。

我们已经是在最长路径上改造边了,而最长路径是长于任何一条路径的(废话),那么经过该边的任何路径在此边被改造后依然短于最长路径,故不用考虑。

至于求两端的最长路径,我们可以前缀和后缀分别维护一下。


那么怎么求前缀(后缀)呢?

我们可以在每个点开个vector,对于一条路径,我们把另一端点以及路径的编号加到vector中。

然后,再开个vis数组,表示这个点是否被访问过。

在遍历时,我们访问所有以这一点为一端点的路径标号\(id\)和另一端点\(y\),若另\(vis[y]=true\),就拿\(id\)的长度去更新即可。(口胡不清,这最好手动模拟一下)

至于求路径长和LCA呢?

额,树剖我是当常数看的。。。

实在不行你可以Tarjan离线预处理啊

代码(巨丑无比):

#include<bits/stdc++.h> #define reg register int #define MAXN 300010 using namespace std; int n,m,head[MAXN],dis[MAXN],tot,maxx,bh,pre[MAXN],sumfr[MAXN],sumla[MAXN],num[MAXN],lian[MAXN],ans=2e9+7; bool mark[MAXN],vis[MAXN]; struct node {     int st,ed,lca,d; } P[MAXN]; struct Edge {//前向星     int ed,v,last; } G[MAXN*2]; struct que {     int ed,id; }; vector<que> Q[MAXN]; struct s__ {//树剖预处理     int son[MAXN],size[MAXN],top[MAXN],deep[MAXN];     void DFS1(int x,int fa,int v) {         dis[x]=dis[fa]+v;         pre[x]=fa;         deep[x]=deep[fa]+1;         size[x]=1;         for(int i=head[x]; ~i; i=G[i].last) {             int t=G[i].ed,v=G[i].v;             if(t==fa)continue;             DFS1(t,x,v);             size[x]+=size[t];             if(size[son[x]]<size[t])son[x]=t;         }     }     void DFS2(int x,int fa,int zu) {         top[x]=zu;         if(son[x])DFS2(son[x],x,zu);         for(int i=head[x]; ~i; i=G[i].last) {             int t=G[i].ed;             if(t==son[x]||t==fa)continue;             DFS2(t,x,t);         }     }     int LCA(int x,int y) {         while(top[x]!=top[y]) {             if(deep[top[x]]<deep[top[y]])swap(x,y);             x=pre[top[x]];         }         if(deep[x]>deep[y])swap(x,y);         return x;     } } shupou; void Rd(int &res) {//读优     res=0;     char ch=getchar();     while('0'>ch||ch>'9')ch=getchar();     while('0'<=ch&&ch<='9')res=(res<<3)+(res<<1)+(ch-'0'),ch=getchar(); } void Add(int st,int ed,int v) {     tot++;     G[tot]=Edge {ed,v,head[st]};     head[st]=tot; } void DFS(int x,int fa,int &bb) {     num[x]=bb;     for(int i=head[x]; ~i; i=G[i].last) {         int t=G[i].ed;         if(t==fa)continue;         if(!mark[t])continue;         lian[bb]=G[i].v;         bb++;         DFS(t,x,bb);     } } void DFSla(int x,int fa,int zu) {//后缀     vis[x]=1;     for(int i=0; i<Q[x].size(); i++) {//访问vector         int t=Q[x][i].ed,id=Q[x][i].id;         if(vis[t]==1)sumla[zu]=max(sumla[zu],P[id].d);//更新     }     int nex=0;     for(int i=head[x]; ~i; i=G[i].last) {         int t=G[i].ed,v=G[i].v;         if(t==fa)continue;         if(mark[t]) {//优先遍历两旁伸出的子树             nex=t;             continue;         }         DFSla(t,x,zu);     }     if(nex) {         sumla[num[nex]]=sumla[num[x]];//更新下一个的后缀         if(nex==P[bh].st)return;//下一个点如果是另一端点的话就直接退出         DFSla(nex,x,num[nex]);     } } void DFSfr(int x,int fa,int zu) {//前缀     vis[x]=1;     for(int i=0; i<Q[x].size(); i++) {//访问vector         int t=Q[x][i].ed,id=Q[x][i].id;         if(vis[t]==1)sumfr[zu]=max(sumfr[zu],P[id].d);//更新     }     int nex=0;     for(int i=head[x]; ~i; i=G[i].last) {         int t=G[i].ed,v=G[i].v;         if(t==fa)continue;         if(mark[t]) {//优先遍历两旁伸出的子树             nex=t;             continue;         }         DFSfr(t,x,zu);     }     if(nex) {         sumfr[num[nex]]=sumfr[num[x]];//更新下一个的前缀         if(nex==P[bh].ed)return;//下一个点如果是另一端点的话就直接退出         DFSfr(nex,x,num[nex]);     } } int main() {     memset(head,-1,sizeof(head));     Rd(n),Rd(m);     for(int i=1; i<=n-1; i++) {         int x,y,z;         Rd(x),Rd(y),Rd(z);         Add(x,y,z);         Add(y,x,z);     }     shupou.DFS1(1,0,0);     shupou.DFS2(1,0,1);     for(int i=1; i<=m; i++) {         Rd(P[i].st),Rd(P[i].ed);         P[i].lca=shupou.LCA(P[i].st,P[i].ed);         P[i].d=dis[P[i].st]+dis[P[i].ed]-2*dis[P[i].lca];         if(P[i].d>maxx)maxx=P[i].d,bh=i;         Q[P[i].st].push_back(que {P[i].ed,i});         Q[P[i].ed].push_back(que {P[i].st,i});     }     int st=P[bh].st,ed=P[bh].ed,lca=P[bh].lca;     while(st!=lca)mark[st]=true,st=pre[st];     while(ed!=lca)mark[ed]=true,ed=pre[ed];     mark[lca]=true;     int bb=1;     DFS(P[bh].st,0,bb);     DFSfr(P[bh].st,0,num[P[bh].st]);     memset(vis,false,sizeof(vis));     DFSla(P[bh].ed,0,num[P[bh].ed]);     for(int i=1;i<=bb;i++){//更新最终答案         int res=0;         res=max(res,P[bh].d-lian[i]);         res=max(res,sumfr[i]);         res=max(res,sumla[i+1]);         ans=min(ans,res);     }     cout<<ans;     return 0; }
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!