[bzoj3047] Freda的传呼机

我从未见过有人写完了树上的算法把50分的环套树的分扔掉的。
就是这样。。。。

口胡题解:
~~~题目询问两个点之间的最短路
1、对于A组数据,也就是树上的数据,直接倍增,然后查询的时候求一个lca然后在把两条路径加上即可
2、B组数据,环套树,把环随意拆掉一个边,树上做三组询问:
①查询两个点之间的最短路,与树上相同
②查询拆掉的边的两个端点到起点和终点的距离
3、C组数据,一个仙人掌(smg),直接把环内距离拆开,拆成一个树,然后倍增。这里要注意一下环内部点的处理:
令l[x],r[x]分别为环内的一个点到达环顶点的距离,那么两个环内的点x,y的距离为:
min{l[x]+r[y],l[y]+r[x],abs(l[x]-l[y])};

正确的仙人掌:(困着打代码1小时,醒着查错5小时。。。)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

const int maxn=121000;
int head[maxn],cnt;
struct node{
    int next,to,w;
}a[maxn<<1];
int n,m,q;
int dfn[maxn];
int pre[maxn][2],lop[maxn][4];
int ttt,lpnum;
int ss;
int eages[maxn][3];
inline void add(int u,int v,int w){
    a[cnt].to=v;
    a[cnt].w=w;
    a[cnt].next=head[u];
    head[u]=cnt++;
}
int ct;
int head2[maxn];
struct node2{
    int next,to,w;
}e[maxn<<1];
inline void ins(int u,int v,int w){
    e[ct].to=v;
    e[ct].w=w;
    e[ct].next=head2[u];
    head2[u]=ct++;
}
inline void dfs(int x,int y,int fa){
    dfn[x]=++ttt;
    for(int i=head[x];~i;i=a[i].next){
        int k=a[i].to;
        if(k==fa&&i==(y^1)) continue;
        if(dfn[k]!=0&&dfn[k]<dfn[x]){
            int len=a[i].w;
            lpnum++;
            for(int j=ss;pre[j][0]!=k;j--)
                len+=pre[j][1];
            lop[x][0]=lpnum;
            lop[x][1]=k;
            lop[x][2]=a[i].w;
            lop[x][3]=len-a[i].w;
            ins(k,x,min(lop[x][2],lop[x][3]));
            ins(x,k,min(lop[x][2],lop[x][3]));
            for(int l=ss-1;pre[l][0]!=k;l--){
                int z=pre[l][0];
                lop[z][0]=lpnum;
                lop[z][1]=k;//环顶
                lop[z][2]=lop[pre[l+1][0]][2]+pre[l+1][1];
                lop[z][3]=len-lop[z][2];
                ins(k,z,min(lop[z][2],lop[z][3]));
                ins(z,k,min(lop[z][2],lop[z][3]));
            }
            continue;
        }
        if(dfn[a[i].to])
            continue;
        pre[++ss][0]=k;
        pre[ss][1]=a[i].w;
        dfs(k,i,x);
    }       
    ss--;
}

int fa[maxn][20];
int bin[20];
inline void init(){
    bin[0]=1;
    for(int i=1;i<=18;i++)
        bin[i]=bin[i-1]<<1;
}
int dis[maxn][20];
int dep[maxn];

inline void dfss(int x){
    for(int i=1;i<=18;i++){
        if(dep[x]<bin[i]) break;
        fa[x][i]=fa[fa[x][i-1]][i-1];
        dis[x][i]=dis[fa[x][i-1]][i-1]+dis[x][i-1];
    }
    for(int i=head2[x];~i;i=e[i].next){
        if(e[i].to!=fa[x][0]){
            //cout<<x<<"  "<<e[i].to<<" "<<e[i].w<<endl; 
            fa[e[i].to][0]=x;
            dep[e[i].to]=dep[x]+1;
            dis[e[i].to][0]=e[i].w;
            dfss(e[i].to);
        }
    }
}
int lca(int x,int y){
    int sum=0;
    if(dep[x]<dep[y]) swap(x,y);
    int t=dep[x]-dep[y];
    for(int i=0;i<=18;i++){
        if(t&bin[i]){
            sum+=dis[x][i];
            x=fa[x][i];
        }
    }
    for(int i=18;i>=0;i--){
        if(fa[x][i]!=fa[y][i]){
            sum+=dis[x][i]+dis[y][i];
            x=fa[x][i];
            y=fa[y][i];
        }
    }
    if(x==y) return sum;
    if(x!=y&&lop[x][0]==lop[y][0]&&lop[x][0]!=0)
        sum+=min(lop[x][2]+lop[y][3],min(lop[x][3]+lop[y][2],abs(lop[x][2]-lop[y][2])));
    else sum+=dis[x][0]+dis[y][0];
    return sum;
}
int ans;
int main(int argc,const char * argv[]){
    ios::sync_with_stdio(false);
    memset(head,-1,sizeof(head));
    memset(head2,-1,sizeof(head2));
    init();
    cin>>n>>m>>q;
    int x,y,z;
    for(int i=1;i<=m;i++){
        cin>>x>>y>>z;
        add(x,y,z);
        add(y,x,z);
        eages[i][1]=x;
        eages[i][2]=y;
        eages[i][0]=z;
    }
    //work on cactus
    ss=1;
    pre[1][0]=1;
    pre[1][1]=0;
    dfs(1,-1,0);
    //cout<<"yes"<<endl;
    for(int i=1;i<=m;i++){
        int aa=eages[i][1];
        int bb=eages[i][2];
        int cc=eages[i][0];
        if((lop[aa][0]!=lop[bb][0]||!lop[aa][0]||!lop[bb][0])&&lop[aa][1]!=bb&&lop[bb][1]!=aa)
            ins(aa,bb,cc),ins(bb,aa,cc);
    }
    //cout<<"yes"<<endl;
    //dep[1]=1;
    dfss(1);
    //cout<<"yes"<<endl;
    for(int i=1;i<=q;i++){
        cin>>x>>y;
        ans=lca(x,y);
        printf("%d\n",ans);
    }
    //for(int i=1;i<=4;i++)
        //cout<<lop[x][0]<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值