Maximum and Minimum
题解
首先,我们需要说明一点,我们会选择的路径一定是最小生成树上的路径。
不然如果有两条路径拥有同样的起点与终点,而较小的路径不在最小生成树上,那就证明另外一条路径上一定有比这条路径大的边,这条边显然是可以删去的。
所以,我们所选择的路径,一定都在最小生成树上。
我们发现我们需要球的是两个点在两个图上路径的最大值的乘积之和,而每条路径又是可以根据点分中心拆成两个部分,而且仅存在一个点分中心既经过有包含该点。
那么对于路径
(
u
,
v
)
(u,v)
(u,v)关于点分
x
x
x,整个路径的最大值为
m
x
(
u
,
v
)
=
max
(
m
x
(
u
,
x
)
,
m
x
(
v
,
x
)
)
mx_{(u,v)}=\max(mx_{(u,x)},mx_{(v,x)})
mx(u,v)=max(mx(u,x),mx(v,x))。
显然,我们可以通过点分治,对其中的一棵树进行维护。
那么我们要求的是,每次我们点分的子树上,两个经过根相连的点,在
G
2
G2
G2上路径的最大值。
这个路径的最大值好维护,我们可以考虑
K
r
u
s
k
a
l
Kruskal
Kruskal重构树。
显然,当我们的边
(
u
,
v
,
w
)
(u,v,w)
(u,v,w)在
K
r
u
s
k
a
l
Kruskal
Kruskal重构树上合并集合
S
1
S_1
S1与
S
2
S_2
S2时,可以发现,那么我们可以发现,这条边的边权
w
w
w,就是集合
S
1
S_1
S1中点到集合
S
2
S_2
S2中点的最大值。
但我们是不好直接合并的,毕竟每次合并的时候如果都要遍历这两个集合中的所有数的话,显然是会
T
T
T飞的。
我们考虑合并的过程,假设我们在合并集合中的
u
u
u与
v
v
v,那么其得到的权值显然应该是
max
(
m
x
u
,
m
x
v
)
\max(mx_u,mx_v)
max(mxu,mxv)。
由于权值是取较大的一个,那我们可以考虑分治的方法来合并这两个集合,分治权值,那么显然,分治左边的数必然是比分治右边的数小的,那么此时这一层的分治贡献的显然是左边的数的个数乘上右边数的权值和。
但显然如果只是这样分治的话并不太能优化这个复杂度,还不如直接把这东西摆到线段树上去,也就是我们每次就直接合并两棵线段树。
在线段树合并的节点上,将右端点与左端点匹配,也就是说在线段树维护一个区间个数和与区间和,合并就将右儿子在的区间和与左儿子的区间个数和相乘。
众所周知,线段树合并的复杂度是均摊的,每次合并函数会减少线段树一个上的一个节点,这样的复杂度就是
O
(
n
log
n
)
O\left(n\log n\right)
O(nlogn)。
虽然这样可能不太好区分是不是在点分树的同一子树上,大不了我们容斥一下,将整棵树的节点匹配后,减去在每棵子树上匹配的就行了。
不过这样的话还是太大了,毕竟我们的点分会有
n
n
n个点分中心,去掉叶子仍然大得离谱。
注意,我们每次点分在第二棵树上真正有影响的节点是远远没有这么多的,因为我们合并的两个集合中真正有意义的节点只有在点分树上的点。
那么我们不妨考虑建一棵虚树,毕竟虚树上的点与有效点的点数是同一级别的,而大部分时候这个。
虚树按
d
f
s
dfs
dfs序排序后,用个栈就可以很简单的建出来,用倍增可以轻易的算出来虚树上每条边的长度,然后再按我们上面讲的方法求出该虚树的贡献就可以了。
总结一下整个过程其实就是先点分,然后对于点分树建虚树,用线段树合并算贡献。看起来代码就很冗杂的样子
可以算出来,时间复杂度为
O
(
n
log
2
n
)
O\left(n\log^2n\right)
O(nlog2n)。
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define MAXM 200005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
typedef long double Ld;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int mo=998244353;
const int mod=1e5+7;
const int inv2=499122177;
const int jzm=2333;
const int zero=2000;
const int lim=100000000;
const int M=100000;
const int orG=3,ivG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-3;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,m,fa[MAXM],dfn[MAXN],rd[MAXN],ord[MAXN],idx,f[MAXN][20],fw[MAXN][20];
int sz[MAXN],mxson[MAXN],Rt,mx,all,root[MAXM],dis[MAXN],dep[MAXN];
int sta[MAXM],stak,st[MAXN],stk,tot,ans;bool vis[MAXN],key[MAXN];
struct tann{int lson,rson,cnt,sum;};
struct ming{int u,v,w;}s[MAXM];
vector<pii>G1[MAXN],G2[MAXN];
vector<int>vec[MAXN];
bool cmp1(ming x,ming y){return x.w<y.w;}
bool cmp2(int x,int y){return dfn[x]<dfn[y];}
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i;}
int findSet(int x){return fa[x]==x?x:fa[x]=findSet(fa[x]);}
void unionSet(int a,int b){int u=findSet(a),v=findSet(b);if(u^v)fa[u]=v;}
class SegmentTree{
private:
tann tr[MAXN*30];int tott;
void pushup(int rt){
tr[rt].sum=add(tr[tr[rt].lson].sum,tr[tr[rt].rson].sum,mo);
tr[rt].cnt=tr[tr[rt].lson].cnt+tr[tr[rt].rson].cnt;
}
public:
void insert(int &rt,int l,int r,int ai){
if(l>r||l>ai||r<ai)return ;int mid=l+r>>1;if(!rt)rt=++tott;
if(l==r){Add(tr[rt].sum,ai,mo);tr[rt].cnt++;return ;}
if(ai<=mid)insert(tr[rt].lson,l,mid,ai);
if(ai>mid)insert(tr[rt].rson,mid+1,r,ai);
pushup(rt);
}
pii merge(int x,int y,int l,int r){
if(!x||!y)return mkpr(x+y,0);int mid=l+r>>1,res=0;
if(l==r){
res=1ll*tr[x].sum*tr[y].cnt%mo;
Add(tr[x].sum,tr[y].sum,mo);
Add(tr[x].cnt,tr[y].cnt,mo);
return mkpr(x,res);
}
if(tr[x].lson&&tr[y].rson)Add(res,1ll*tr[tr[x].lson].cnt*tr[tr[y].rson].sum%mo,mo);
if(tr[x].rson&&tr[y].lson)Add(res,1ll*tr[tr[y].lson].cnt*tr[tr[x].rson].sum%mo,mo);
pii tmpl=merge(tr[x].lson,tr[y].lson,l,mid);
tr[x].lson=tmpl.fir;Add(res,tmpl.sec,mo);
pii tmpr=merge(tr[x].rson,tr[y].rson,mid+1,r);
tr[x].rson=tmpr.fir;Add(res,tmpr.sec,mo);
pushup(x);return mkpr(x,res);
}
void clear(){for(int i=1;i<=tott;i++)tr[i].cnt=tr[i].sum=tr[i].lson=tr[i].rson=0;tott=0;}
}T;
void dosaka1(int u,int fa){
f[u][0]=fa;dep[u]=dep[fa]+1;dfn[u]=++idx;ord[idx]=u;
for(int i=1;i<19;i++)
f[u][i]=f[f[u][i-1]][i-1],
fw[u][i]=max(fw[u][i-1],fw[f[u][i-1]][i-1]);
int siz=G2[u].size();
for(int i=0;i<siz;i++){
int v=G2[u][i].fir;if(v==fa)continue;
fw[v][0]=G2[u][i].sec;dosaka1(v,u);
}
rd[u]=idx;
}
void getRoot(int u,int fa){
sz[u]=1;mxson[u]=0;int siz=G1[u].size();
for(int i=0;i<siz;i++){
int v=G1[u][i].fir;if(v==fa||vis[v])continue;
getRoot(v,u);sz[u]+=sz[v];
mxson[u]=max(mxson[u],sz[v]);
}
mxson[u]=max(mxson[u],all-sz[u]);
if(mx>mxson[u])mx=mxson[u],Rt=u;
}
void dosaka2(int u,int fa,int ori){
int siz=G1[u].size();sz[u]=1;
vec[ori].pb(u);if(fa)vec[Rt].pb(u);
for(int i=0;i<siz;i++){
int v=G1[u][i].fir;if(vis[v]||v==fa)continue;
dis[v]=max(dis[u],G1[u][i].sec);
dosaka2(v,u,fa?ori:v);sz[u]+=sz[v];
}
}
int lca(int a,int b){
if(dep[a]>dep[b])swap(a,b);
for(int i=18;i>=0;i--)if(dep[f[b][i]]>=dep[a])b=f[b][i];
if(a==b)return a;
for(int i=18;i>=0;i--)if(f[a][i]^f[b][i])a=f[a][i],b=f[b][i];
return f[a][0];
}
int findVal(int x,int dp){
int res=0;
for(int i=18;i>=0;i--)if((dp>>i)&1)
res=max(res,fw[x][i]),x=f[x][i];
return res;
}
void work(int id,int typ){
int siz=vec[id].size(),res=0;stak=stk=0;
for(int i=0;i<siz;i++)sta[++stak]=vec[id][i],key[sta[stak]]=1;
sort(sta+1,sta+stak+1,cmp2);int tmps=stak;tot=0;
for(int i=1;i<tmps;i++)sta[++stak]=lca(sta[i],sta[i+1]);
sort(sta+1,sta+stak+1,cmp2);stak=unique(sta+1,sta+stak+1)-sta-1;
for(int i=1;i<=stak;i++){
while(stk&&rd[sta[st[stk]]]<dfn[sta[i]])stk--;
if(stk)s[++tot]=(ming){st[stk],i,findVal(sta[i],dep[sta[i]]-dep[sta[st[stk]]])};
st[++stk]=i;
}
sort(s+1,s+tot+1,cmp1);makeSet(stak);int idp=stak;
for(int i=1;i<=stak;i++)root[i]=0;
for(int i=1;i<=stak;i++)if(key[sta[i]])T.insert(root[i],0,lim,dis[sta[i]]);
for(int i=1;i<=tot;i++){
int u=s[i].u,v=s[i].v,w=s[i].w;
u=findSet(u),v=findSet(v);
int p=++idp;fa[p]=fa[u]=fa[v]=p;
pii tmp=T.merge(root[u],root[v],0,lim);
root[p]=tmp.fir;Add(res,1ll*w*tmp.sec%mo,mo);
}
while(stak)key[sta[stak--]]=0;T.clear();
Add(ans,1ll*typ*res%mo,mo);
}
void sakura(int rt){
mx=INF;getRoot(rt,0);dis[Rt]=0;vis[Rt]=1;dosaka2(Rt,0,Rt);
work(Rt,1);vec[Rt].clear();int siz=G1[Rt].size(),trt=Rt;
for(int i=0;i<siz;i++){
int v=G1[trt][i].fir;if(vis[v])continue;
work(v,mo-1);vec[v].clear();
}
for(int i=0;i<siz;i++){
int v=G1[trt][i].fir;if(vis[v])continue;
all=sz[v];sakura(v);
}
}
signed main(){
read(n);read(m);
for(int i=1;i<=m;i++)read(s[i].u),read(s[i].v),read(s[i].w);
sort(s+1,s+m+1,cmp1);makeSet(n);int cnt=0;
for(int i=1;i<=m;i++){
int u=s[i].u,v=s[i].v,w=s[i].w;
if(findSet(u)==findSet(v))continue;unionSet(u,v);
G1[u].pb(mkpr(v,w));G1[v].pb(mkpr(u,w));cnt++;
}
for(int i=1;i<=m;i++)read(s[i].u),read(s[i].v),read(s[i].w);
sort(s+1,s+m+1,cmp1);makeSet(n);
for(int i=1;i<=m;i++){
int u=s[i].u,v=s[i].v,w=s[i].w;
if(findSet(u)==findSet(v))continue;unionSet(u,v);
G2[u].pb(mkpr(v,w));G2[v].pb(mkpr(u,w));cnt++;
}
dosaka1(1,0);all=n;sakura(1);
printf("%d\n",ans);
return 0;
}