[JZOJ 3395] Freda的传呼机

来源:互联网 发布:数据库事务特性 老婆 编辑:程序博客网 时间:2024/06/05 20:08

Description

给定一个有N个点,M条边的图,有Q个询问,每次询问两个点之间的最短距离。
对于100%的数据,2<=N<=10000N1<=M<=12000Q=10000
时间限制100ms
啊,我不会做!
嗯,当然,还有一点忘说了,只有三类数据:

  1. M=N-1(树)30%
  2. M=N(环套外向树)50%
  3. 每条边仅在一个简单环中(仙人掌)10%

什么,你问30%+50%+10%=90%!=100%,其实剩下那10%是送的。

Analysis

这是本蒟蒻的第一道仙人掌。
显然,仙人掌就包括了前两种情况,但是出题人毒瘤,不仅出这种业界毒瘤,仙人掌还只给10分。
这里有一种把仙人掌变成树的方法,感谢alan大神百度搜索提供的资料,搜索技术高超。像我这种蒟蒻搜到的全都是
这里写图片描述
真是美丽的仙人掌啊!
好吧,废话不说了。
上面的方法形象地解释就是这样:
这里写图片描述
定义环顶为一个环里dfn最小的点。
如图,设红色的点是环顶。
若两个点在同一个环中,如下图两个蓝色的点
这里写图片描述
那他们的距离显然是从环的两边走的较小的那一个。
所以我们要求出每个环的大小size,以及从一边走的距离dis,这样两个在同一个环里的点的距离就是min(dis,sizedis)
当然,我们变成树之后,设询问的两个点为x,y,lca(x,y)=lcau,v表示lca的两个儿子。这样子说不清,如下图:
这里写图片描述
u,v在不在同一环中,则直接dis[x]+dis[y]2dis[lca],当然,dissp(b)fa搞出来。
否则,x,y不在同一个环中,所以直接用dis[x]-dis[u]+dis[y]-dis[v]+(u到v环上的距离)即可。

Code

#include<cstdio>#include<cmath>#include<cstring>#include<queue>#include<algorithm>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,b,a) for(int i=b;i>=a;i--)#define efo(i,v) for(int i=last[v];i;i=next[i])using namespace std;const int N=10010,M=N*5;int tot,n,m,num,st[M],to[M],next[M],wei[M],last[N];int now,dep[N],dis[N],di[N],from[N],dfn[N],val[N],lyd[N],f[N][15];bool bz[M],vis[M];queue<int> q;void link(int u,int v,int w){    st[++tot]=u,to[tot]=v,wei[tot]=w,next[tot]=last[u],last[u]=tot;}void spfa(){    q.push(1);    bz[1]=1;    memset(dis,60,sizeof(dis));    dis[1]=0;    while(!q.empty())    {        int u=q.front();q.pop();        bz[u]=0;        efo(i,u)        {            int v=to[i];            if(dis[u]+wei[i]<dis[v])            {                dis[v]=dis[u]+wei[i];                if(!bz[v])                {                    bz[v]=1;                    q.push(v);                }            }        }    }}void rt(int i,int t){    ++num;    while(1)    {        int v=to[i];        if(to[i]!=t && st[i]!=t)        {            bz[i]=bz[i^1]=1;            link(v,t,0),link(t,v,0);        }        val[num]+=wei[i];        if(st[i]!=t) lyd[st[i]]=num;        if(st[i]==t) break;        i=from[st[i]];    }}void dfs1(int v,int fr){    dfn[v]=++now;    efo(i,v)    {        int u=to[i];        if(u==fr || bz[i]) continue;        if(!dfn[u])        {            from[u]=i,di[u]=di[v]+wei[i];            dfs1(u,v);        }        else        if(dfn[u]<dfn[v]) rt(i,u);    }}void dfs2(int v,int fr,int k){    vis[v]=1;    dep[v]=k,f[v][0]=fr;    efo(i,v)    {        int u=to[i];        if(bz[i] || u==fr || vis[u]) continue;        dfs2(u,v,k+1);    }}int getlca(int &u,int &v){    if(dep[u]>dep[v]) swap(u,v);    fd(i,int(log2(dep[v])),0)        if(dep[f[v][i]]>=dep[u]) v=f[v][i];    if(u==v) return u;    fd(i,int(log2(dep[v])),0)        if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];    return f[u][0];}int main(){    int _,x,y,u,v,w;    scanf("%d %d %d",&n,&m,&_);    tot=1;    fo(i,1,m)    {        scanf("%d %d %d",&u,&v,&w);        link(u,v,w),link(v,u,w);    }    spfa();    dfs1(1,1);    dfs2(1,1,1);    fo(j,1,int(log2(n)))        fo(i,1,n) f[i][j]=f[f[i][j-1]][j-1];    while(_--)    {        scanf("%d %d",&x,&y);        int lca=getlca(u=x,v=y);        if(lyd[u] && lyd[v] && lyd[u]==lyd[v])        {            int k=abs(di[u]-di[v]);            k=min(k,val[lyd[u]]-k);            printf("%d\n",dis[x]+dis[y]-dis[u]-dis[v]+k);        }        else        printf("%d\n",dis[x]+dis[y]-2*dis[lca]);    }    return 0;}
0 1