1 条题解

  • 0
    @ 2025-8-24 21:51:24

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar zzwdsj
    1+2不等于3!

    搬运于2025-08-24 21:51:24,当前版本为作者最后更新于2025-02-08 11:10:12,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    测试点下载:测试点.zip

    思路

    两个结论:

    • 异色点对的最短距离一定是一条边。 证明:当一条路径上的边两端颜色均相同是,这条路径两端颜色也相同,所以如果一条路径上两端颜色不同,那么这条路径上必有一条边两端颜色不同。那么如果异色点对的最短距离是一条(边数大于一的)路径,那么选这条路径上的一条两端颜色不同的边肯定比选这条路径更优。
    • 异色点对的最短距离一定是一条最小生成树上的边。 证明:如果一条两端异色的边 (u,v)(u,v) 没有被加入最小生成树。uuvv 必有一条只经过最小生成树上边的路径,且这条路径上的边权均不大于 (u,v)(u,v) 的边权,与 (u,v)(u,v) 形成环。已知点 uu 与点 vv 异色,那么 uuvv 只经过最小生成树上边的路径上必有一条两端异色的边 (u0,v0)(u_0,v_0)(u0,v0)(u_0,v_0) 的边权比 (u,v)(u,v) 小,所以选 (u0,v0)(u_0,v_0)(最小生成树上的边)一定更优。

    minni,j\min n_{i,j} 为最小生成树上点 ii 的儿子中颜色为 jj 的儿子连接点 ii 的边权构成的可重集;disidis_i 为任意 jjminni,j\min n_{i,j} 不为空,ii 的颜色不为 jjminni,j\min n_{i,j} 中的最小值构成的可重集;ansans 为任意 ii (disidis_i 不为空)disidis_i 中的最小值最小值构成的可重集。

    建树时。先跑一遍 Kruskal,求出原图的最小生成树,只保留生成树上的边。然后跑 dfs 按照定义求出 minni,j\min n_{i,j}disidis_iansans,同时记下每个点的父亲记作 fif_i,每个点与父亲相连的边的边权记作 lil_i,每个点的颜色记作 viv_i

    将点 xx 的颜色修改为 jj 时,进行一下操作以更新 minni,j\min n_{i,j}disidis_iansans

    1. 更新 ansans:由于在下面修改了 disfxdis_{f_x}disfxdis_{f_x} 可能有新的最小值,应该删掉原本的最小值。在 ansans 中删除 disfxdis_{f_x} 的最小值。
    2. xxfxf_x 异色时,更新 disfxdis_{f_x}:由于在操作 3 中更新了 minnfx,vx\min n_{f_x,v_x}minnfx,vx\min n_{f_x,v_x} 可能有新的最小值,而当 xxfxf_x 异色时 minnfx,vx\min n_{f_x,v_x} 的最小值对 disfxdis_{f_x} 有贡献,应该删掉原本的最小值。在 disfxdis_{f_x} 中删除 minnfx,vx\min n_{f_x,v_x} 的最小值(此时直到更新 vxv_xvxv_x 仍是修改前 vxv_x 的颜色)。
    3. 更新 minnfx,vx\min n_{f_x,v_x}:由于修改颜色后,xx 颜色改变了,按照 minnfx,vx\min n_{f_x,v_x} 的定义,lXl_X,不在 minnfx,vx\min n_{f_x,v_x} 里。在 minnfx,vx\min n_{f_x,v_x} 中删除 lxl_x
    4. xxfxf_x 异色时,更新 disfXdis_{f_X}:同 2,由于在操作 3 中更新了 minnfx,vx\min n_{f_x,v_x}minnfx,vx\min n_{f_x,v_x} 可能有新的最小值。而当 xxfxf_x 异色时 minnfx,vx\min n_{f_x,v_x} 的最小值对 disfxdis_{f_x} 有贡献,应该加上现在的最小值。在 disfxdis_{f_x} 中插入 minnfx,vx\min n_{f_x,v_x} 的最小值(此时直到更新 vxv_xvxv_x 仍是修改前 vxv_x 的颜色)。
    5. 更新 ansans:由于在 6 和 8 中修改了 disxdis_xdisxdis_x 可能有新的小值,应该删掉原本的最小值。在 ansans 中删除 disxdis_x 的最小值。
    6. 更新 disxdis_x:由于修改颜色后,xx 的颜色可能不为 vxv_x,那么 xx 的子节点中颜色为 vxv_x 的节点可能可以对 disxdis_x 产生贡献。在 disxdis_x 中插入 minnx,vx\min n_{x,v_x} 的最小值。
    7. 更新 vxv_x : 将 vxv_x 更新为 yy
    8. 更新 disxdis_x:由于修改颜色后,xx 的儿子中与 vxv_x 同色的儿子不能对 disxdis_x 产生贡献,但之前考虑了它的贡献。在 disxdis_x 中删除 minnx,vx\min n_{x,v_x} 的最小值。(此后的 vxv_xxx 修改后的颜色)
    9. 更新 ansans:同 5,由于在 6 和 8 中修改了 disxdis_xdisxdis_x 可能有新的小值,应该加上现在的最小值。在 ansans 插入 disxdis_x 的最小值。
    10. xxfxf_x 异色时,更新 disfXdis_{f_X}:由于在 11 中修改了 minnfx,vx\min n_{f_x,v_x}minnfx,vx\min n_{f_x,v_x} 可能有新的小值,应该删掉原本的最小值。在 disfXdis_{f_X} 中删除 minnfx,vx\min n_{f_x,v_x} 的最小值。
    11. 更新 minnfx,vx\min n_{f_x,v_x}:由于修改颜色后,xx 颜色改变了,按照 minnfx,vx\min n_{f_x,v_x} 的定义,lXl_X,在 minnfx,vx\min n_{f_x,v_x} 里。在 minnfx,vx\min n_{f_x,v_x} 中插入 lxl_x
    12. xxfxf_x 异色时,更新 disfXdis_{f_X}:由于在 11 中修改了 minnfx,vx\min n_{f_x,v_x}minnfx,vx\min n_{f_x,v_x} 可能有新的小值,应该加上现在的最小值。在 disfXdis_{f_X} 插入 minnfx,vx\min n_{f_x,v_x} 的最小值。
    13. 更新 ansans:由于在上面修改了 disfxdis_{f_x}disfxdis_{f_x} 可能有新的最小值,应该加上现在的最小值。在 ansans 插入 disfxdis_{f_x} 的最小值。

    code

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,k,q,v[200005],l[200005],fa[200005]/*用于存储并查集中每个节点的父亲*/,f[200005],x,y;
    vector<pair<int,int> >mp[200005];
    multiset<int>dis[200005],ans;
    unordered_map<int,multiset<int> >minn[200005];
    //定义见上文
    struct edge
    {
    	int x,y,l;
    	bool operator<(const edge t)const{return l<t.l;}	
    }e[200005];
    int find(int x)
    {
    	if(fa[x]!=x)fa[x]=find(fa[x]);
    	return fa[x];
    }
    //并查集的实现
    void dfs(int x,int fa)
    {
    	f[x]=fa;
    	for(pair<int,int> i:mp[x])if(i.first!=fa) minn[x][v[i.first]].insert(i.second),l[i.first]=i.second,dfs(i.first,x);
        for(auto i:minn[x])if(i.first!=v[x])dis[x].insert(*i.second.begin());
    	if(dis[x].size())ans.insert(*dis[x].begin());
    }
    //按照定义求出 ans,minn[i][j]和dis[i]
    int main()
    {
    	scanf("%d%d%d%d",&n,&m,&k,&q);
    	for(int i=1;i<=m;i++)scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].l);
    	for(int i=1;i<=n;i++)scanf("%d",&v[i]);
    	sort(e+1,e+m+1),iota(fa+1,fa+n+1,1);//此函数的功能是把fa[1],fa[2],...,fa[n]依次赋值为1,2,...,n。
    	for(int i=1,cnt=0;i<=m&&cnt<n-1;i++)
    		if(find(e[i].x)!=find(e[i].y))
    			fa[find(e[i].x)]=find(e[i].y),mp[e[i].x].push_back({e[i].y,e[i].l}),mp[e[i].y].push_back({e[i].x,e[i].l}),cnt++;
    	//Kruskal 的过程
    	dfs(1,0);
    	while(q--)
    	{
    		scanf("%d%d",&x,&y);
    		if(f[x])
    		{
    			if(dis[f[x]].size())ans.erase(ans.find(*dis[f[x]].begin()));//见上文操作1
    			if(v[x]!=v[f[x]])dis[f[x]].erase(dis[f[x]].find(*minn[f[x]][v[x]].begin()));//见上文操作2
    			minn[f[x]][v[x]].erase(minn[f[x]][v[x]].find(l[x]));//见上文操作3
    			if(v[x]!=v[f[x]]&&minn[f[x]][v[x]].size())dis[f[x]].insert(*minn[f[x]][v[x]].begin());//见上文操作4
    		}
    		if(dis[x].size())ans.erase(ans.find(*dis[x].begin()));//见上文操作5
    		if(minn[x][v[x]].size())dis[x].insert(*minn[x][v[x]].begin());//见上文操作6
    		v[x]=y;//见上文操作7
    		if(minn[x][v[x]].size()&&dis[x].size())dis[x].erase(dis[x].find(*minn[x][v[x]].begin()));//见上文操作8
    		if(dis[x].size())ans.insert(*dis[x].begin());//见上文操作9
    		if(f[x])
    		{
    			if(v[x]!=v[f[x]]&&minn[f[x]][v[x]].size())dis[f[x]].erase(dis[f[x]].find(*minn[f[x]][v[x]].begin()));//见上文操作10
    			minn[f[x]][v[x]].insert(l[x]);//见上文操作11
    			if(v[x]!=v[f[x]])dis[f[x]].insert(*minn[f[x]][v[x]].begin());//见上文操作12
    			if(dis[f[x]].size())ans.insert(*dis[f[x]].begin());//见上文操作13
    		}
    		printf("%d\n",*ans.begin());
    	}
    	return 0;
    }
    
    • 1

    信息

    ID
    1112
    时间
    2000ms
    内存
    500MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者