hdu6730 APSP on Cactus【百度之星2019复赛】【仙人掌】【最短路】

题目大意

 给出一个仙人掌,求每个点 i i i到其他所有点的最短距离和 a n s [ i ] ans[i] ans[i],边有边权。
n ≤ 2 e 5 n\le 2e5 n2e5

题解

 假如是一棵树的话,我们可以通过计算出每条边贡献来旋根 d p dp dp(可能叫计数比较好一点)
 仙人掌也同理,考虑 u − v u-v uv这条边,怎么计算 a n s [ v ] − a n s [ u ] ans[v]-ans[u] ans[v]ans[u].

  • u − v u-v uv不在任意一个环上
     这种情况和普通的树是一样的,答案增量是 边 长 ∗ ( v 这 边 子 树 的 大 小 − u 这 边 子 树 的 大 小 ) 边长*(v这边子树的大小-u这边子树的大小) (vu)
  • u − v u-v uv在某个环上
     我们注意到,由上面的做法,环头的答案是已经知道了的。
     而如果起点从 u u u移动到 v v v的话,走出环后的最短路是不会改变的,所以我们只需要知道起点从 u u u移动到 v v v后,环上的最短路怎么变就好了。显然环上会有一个分界点,而我们又是可以通过双指针来知道这个分界点在哪的,然后就做完了。

 复杂度 O ( n ) O(n) O(n).

 代码写得有点挫,大佬们见谅。

#include<bits/stdc++.h>
#define maxn 400050
#define modu 1000000007
using namespace std;
typedef long long LL;
typedef pair<int,int> Edge;

ostream& operator << (ostream& os,Edge e)   {
    os<<"("<<e.first<<","<<e.second<<")";
    return os;
}

int n,m;
vector<Edge> G[maxn];
Edge sp[maxn],bom[maxn];

int fa[maxn];
int anc[maxn];

int dep[maxn],tsz[maxn];

LL ans[maxn];

LL diff[maxn];

int read()  {
    int x=0;
    char c=getchar();
    while (c<'0'||c>'9') c=getchar();
    while ('0'<=c&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return x;
}

void init() {
    n=read(),m=read();
    for (int i=1;i<=n;++i) G[i].clear();

    while (m--) {
        int u,v,w;
        u=read(),v=read(),w=read();
        G[u].emplace_back(v,w);
        G[v].emplace_back(u,w);
    }

    for (int i=1;i<=n;++i) fa[i]=anc[i]=dep[i]=tsz[i]=0;
    for (int i=1;i<=n;++i) sp[i]=bom[i]=Edge(0,0);
    for (int i=1;i<=n;++i) ans[i]=diff[i]=0;
}

int dfs1(int i)   {
    tsz[i]=1;
    dep[i]=dep[fa[i]]+1;
    for (Edge e:G[i])  {
        int j=e.first;
        if (fa[i]==j) continue;
        if (dep[j]) {
            if (dep[j]>dep[i]) continue;
            anc[i]=j,bom[i]=e;
        }
        else    {
            fa[j]=i;
            int t=dfs1(j);
            tsz[i]+=tsz[j];
            if (t) sp[j]=e;
            if (t&&t!=i)
                anc[i]=t;
        }
    }
    return anc[i];
}

int sub[maxn];

Edge C[maxn];

LL con[maxn];
LL L[maxn],R[maxn],S[maxn],len[maxn];

void deal(int v)    {
    int u=anc[v],m=0;
    for (int t=v;t!=u;t=fa[t])
        C[++m]=sp[t];
    C[++m]=bom[v];

    
    sub[u]=n;
    for (int i=1;i<m;++i)
        sub[C[i].first]=tsz[C[i].first]-tsz[C[i-1].first],sub[u]-=sub[C[i].first];
    reverse(C+1,C+m+1);

    LL sum=0;
    for (int i=1;i<=m;++i)
        C[i+m]=C[i],sum+=C[i].second;

    for (int i=1;i<=2*m;++i)    {
        len[i]=len[i-1]+C[i].second;
        L[i]=L[i-1]+len[i]*sub[C[i].first];
        R[i]=R[i-1]+(sum-len[i])*sub[C[i].first];
        S[i]=S[i-1]+sub[C[i].first];
    }
    
    LL l=0,j=0;
    for (int i=1;i<=m;++i)    {
        while (j<i) ++j;
        while ((l+C[j+1].second)*2<=sum)  
            l+=C[++j].second;
        
        con[i]=(L[j]-L[i-1]-(S[j]-S[i-1])*len[i])+(R[i+m-1]-R[j]+(S[i+m-1]-S[j])*len[i]);

        if (i>1)
            diff[C[i].first]=con[i]-con[i-1];
        else
            ans[1]+=con[i];

        if (j>i) l-=C[i+1].second;
    }
}

void dfs2(int i)   {
    if (bom[i].first)
        deal(i);
    for (pair<int,int> e:G[i])  {
        int j=e.first;
        if (dep[j]!=dep[i]+1) continue;
        if (!anc[j])    {
            ans[1]+=(LL)e.second*tsz[j];
            diff[j]=(LL)e.second*(n-2*tsz[j]);
        }
        dfs2(j);
    }
}

void dfs3(int i)    {
    for (Edge e:G[i])   {
        int j=e.first;
        if (dep[j]!=dep[i]+1) continue;
        ans[j]=ans[i]+diff[j];
        dfs3(j);
    }
}

int main()  {

    int T;
    scanf("%d",&T);

    int t1=0,t2=0;
    while (T--) {
        init();
        dfs1(1),dfs2(1),dfs3(1);

        LL ans=0;
        for (int i=1,u=1;i<=n;++i)  {
            u=u*233LL%modu;
            ans=(ans+(::ans[i]^i)%modu*u)%modu;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值