链接:Gym101889
给出 R R R条带权边,构建一棵 N ( 2 ≤ N ≤ 1 0 5 ) N\,(2\le N\le 10^5) N(2≤N≤105)个结点的最小生成树,共 Q ( 1 ≤ Q ≤ 1 0 5 ) Q\,(1\le Q\le 10^5) Q(1≤Q≤105)次询问,每次询问必选边 ( U , V ) (U,V) (U,V)的最小生成树的边权和。
先不考虑必选边,建一棵最小生成树,对于询问必选边 ( U , V ) (U,V) (U,V),找到最小生成树中 U U U, V V V路径上最大边权,然后删除该边,将边 ( U , V ) (U,V) (U,V)加上即可。
对于找到树上 U U U, V V V路径上的最大边权,将 边权 赋到 该边更深结点 的 点权上,然后用树链剖分即可。
#include
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int INF=0x3f3f3f3f;
const int maxn=1e6+10;
int N,R,Q,min_sum=0;
struct road
{
int u;
int v;
int w;
bool friend operator < (const road &x,const road &y)
{
return x.w>y.w;
}
}r[maxn];
struct edge
{
int u;
int v;
int w;
int next;
}e[maxn];
int head[maxn],cnt;
void add_edge(int u,int v,int w)
{
e[cnt]=edge{u,v,w,head[u]};
head[u]=cnt++;
e[cnt]=edge{v,u,w,head[v]};
head[v]=cnt++;
}
map<PII,int> cost;
int father[maxn];
int find_father(int x)
{
int root=x;
while(father[root]!=root)
root=father[root];
while(father[x]!=x)
{
int t=x;
x=father[x];
father[t]=root;
}
return root;
}
void kruskal()
{
priority_queue<road> q;
for(int i=1;i<=R;i++)
q.push(r[i]);
while(!q.empty())
{
int u=q.top().u,v=q.top().v,w=q.top().w;
q.pop();
int Fu=find_father(u),Fv=find_father(v);
if(Fu!=Fv)
{
father[Fu]=Fv;
add_edge(u,v,w);
min_sum+=w;
}
}
}
int fa[maxn],dep[maxn],sz[maxn],son[maxn];
void dfs1(int u,int pre,int depth)
{
fa[u]=pre;
dep[u]=depth;
sz[u]=1;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if(v==pre)
continue;
dfs1(v,u,depth+1);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]])
son[u]=v;
}
}
int id[maxn],top[maxn],wt[maxn],tot=0;
void dfs2(int u,int TOP)
{
top[u]=TOP;
id[u]=++tot;
if(!son[u])
return;
dfs2(son[u],TOP);
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if(v!=son[u]&&v!=fa[u])
dfs2(v,v);
}
}
#define ls rt<<1
#define rs rt<<1|1
int t[maxn<<2];
void build(int rt,int l,int r)
{
if(l==r)
{
t[rt]=wt[l];
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
t[rt]=max(t[ls],t[rs]);
}
int query(int rt,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)
return t[rt];
int mid=(l+r)>>1;
int ans=0;
if(ql<=mid)
ans=max(ans,query(ls,l,mid,ql,qr));
if(qr>mid)
ans=max(ans,query(rs,mid+1,r,ql,qr));
return ans;
}
int tree_query(int x,int y)
{
int ans=0;
while(top[x]!=top[y])
{
if(dep[top[x]]>=dep[top[y]])
{
ans=max(ans,query(1,1,N,id[top[x]],id[x]));
x=fa[top[x]];
}
else
{
ans=max(ans,query(1,1,N,id[top[y]],id[y]));
y=fa[top[y]];
}
}
if(x==y)
return ans;
if(id[x]<id[y])
ans=max(ans,query(1,1,N,id[son[x]],id[y])); //因为把边权给了该边更深点的点权
else //所以同一条重链上的边是从son[x]到y
ans=max(ans,query(1,1,N,id[son[y]],id[x]));
return ans;
}
void init()
{
memset(head,-1,sizeof(head));
cnt=0;
for(int i=1;i<=N;i++)
father[i]=i;
}
int main()
{
scanf("%d %d",&N,&R);
for(int i=1;i<=R;i++)
{
scanf("%d %d %d",&r[i].u,&r[i].v,&r[i].w);
if(r[i].u>r[i].v)
swap(r[i].u,r[i].v);
cost[make_pair(r[i].u,r[i].v)]=r[i].w;
}
init();
kruskal();
dfs1(1,-1,0);
dfs2(1,1);
for(int i=0;i<cnt;i+=2)
{
int u=e[i].u,v=e[i].v,w=e[i].w;
if(dep[u]<dep[v])
wt[id[v]]=w;
else
wt[id[u]]=w;
}
build(1,1,N);
scanf("%d",&Q);
while(Q--)
{
int u,v;
scanf("%d %d",&u,&v);
if(u>v)
swap(u,v);
printf("%d\n",min_sum-tree_query(u,v)+cost[make_pair(u,v)]);
}
return 0;
}