题目大意
给出一个仙人掌,求每个点
i
i
i到其他所有点的最短距离和
a
n
s
[
i
]
ans[i]
ans[i],边有边权。
n
≤
2
e
5
n\le 2e5
n≤2e5
题解
假如是一棵树的话,我们可以通过计算出每条边贡献来旋根
d
p
dp
dp(可能叫计数比较好一点)
仙人掌也同理,考虑
u
−
v
u-v
u−v这条边,怎么计算
a
n
s
[
v
]
−
a
n
s
[
u
]
ans[v]-ans[u]
ans[v]−ans[u].
-
u
−
v
u-v
u−v不在任意一个环上
这种情况和普通的树是一样的,答案增量是 边 长 ∗ ( v 这 边 子 树 的 大 小 − u 这 边 子 树 的 大 小 ) 边长*(v这边子树的大小-u这边子树的大小) 边长∗(v这边子树的大小−u这边子树的大小) -
u
−
v
u-v
u−v在某个环上
我们注意到,由上面的做法,环头的答案是已经知道了的。
而如果起点从 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;
}