zoukankan      html  css  js  c++  java
  • 【洛谷P6628】丁香之路

    题目

    题目链接:https://www.luogu.com.cn/problem/P6628
    春暖花开,万物复苏,随着疫情的逐渐过去,Yazid 带着他的 (n) 个好朋友来到 T 大校园参观游览。方便起见,我们将他们从 (1)(n) 编号。
    T 大校园的版图可以抽象成一张 (n) 个顶点的无向图(顶点编号从 (1)(n))。且对于任意两个不同顶点,设它们的编号分别为 (i, j(i eq j)),则它们之间有一条需要花费 (|i - j|) 单位时间通过的无向边。
    丁香花是 T 大的校花之一。时下正值丁香花盛开之际,校园内的 (m) 条道路上都开有丁香花。Yazid 的朋友们对丁香花十分感兴趣,因此他们都希望遍历所有开有丁香花的 (m) 条道路。
    Yazid 的朋友们从顶点 (s) 出发。其中,第 (i) 个朋友希望以顶点 (i) 为终点终止他的参观。与此同时,如上面所述,每个朋友都必须经过开着丁香花的 (m) 条道路各至少一次。
    Yazid 的朋友不想太过疲累,因此他们希望花尽可能少的时间来完成他们的目标。
    请你计算 Yazid 的朋友们分别需要花费多少单位时间完成他们的目标。
    (nleq 2500,mleq frac{n(n-1)}{2})

    思路

    首先因为这 (m) 条边是必须走的,所以可以直接让答案加上这些边的权值。那么我们的目的就是最小化其他路径的权值之和。
    对于一条 (i o j)(i<j))的路径,其实可以看作是 (i o i+1 o cdots o j)。对于终点是 (t) 的询问,也就是需要我们添加若干这样长度为 (1) 的边,使得存在一条起点是 (s),终点是 (t) 的欧拉路径。
    存在欧拉路径的充要条件是起点和终点的度数为奇数,其他点的度数为偶数。那么我们先让 (s)(t) 的度数都加一。依次枚举 (i),如果此时 (i) 的度数是奇数,那么就连上 (i o i+1) 这一条边。
    这样贪心选择后的代价肯定是最小的。但是现在选出的路径可能并不连通。用并查集缩点,再次枚举所有点 (i),记上一个度数不为 (0) 的点是 (j)(因为度数为 (0) 的点干脆不经过就行),如果 (i)(j) 所在的连通块不同,那么就增加一条长度为 (|i-j|) 的边连接两个连通块。最后求一下最小生成树即可。显然所需要增加的代价就是最小生成树权值的 (2) 倍。
    时间复杂度 (O((n^2+m)log n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=2510,M=3200010;
    int n,m,s,ans,sum,deg[N],deg1[N],father1[N],father[N];
    
    struct edge
    {
    	int u,v,dis;
    }e[M];
    
    void prework()
    {
    	memcpy(deg,deg1,sizeof(deg));
    	memcpy(father,father1,sizeof(father));
    	ans=sum; m=0;
    }
    
    int find(int x)
    {
    	return x==father[x]?x:father[x]=find(father[x]);
    }
    
    bool cmp(edge x,edge y)
    {
    	return x.dis<y.dis;
    }
    
    int main()
    {
    	scanf("%d%d%d",&n,&m,&s);
    	for (int i=1;i<=n;i++) father[i]=i;
    	for (int i=1,x,y;i<=m;i++)
    	{
    		scanf("%d%d",&x,&y);
    		deg1[x]++; deg1[y]++;
    		father[find(x)]=find(y);
    		sum+=abs(x-y);
    	}
    	memcpy(father1,father,sizeof(father));
    	for (int i=1;i<=n;i++)
    	{
    		prework();
    		deg[s]++; deg[i]++;
    		for (int j=1;j<n;j++)
    			if (deg[j]&1)
    			{
    				ans++; deg[j]++; deg[j+1]++;
    				father[find(j)]=find(j+1);
    			}
    		for (int j=1,k=0;j<=n;j++)
    			if (deg[j])
    			{
    				if (k && find(j)!=find(k))
    					e[++m]=(edge){find(j),find(k),j-k};
    				k=j;
    			}
    		sort(e+1,e+1+m,cmp);
    		for (int j=1;j<=m;j++)
    		{
    			int x=find(e[j].u),y=find(e[j].v);
    			if (x!=y) father[x]=y,ans+=2*e[j].dis;
    		}
    		cout<<ans<<" ";
    		deg[s]--; deg[i]--;
    	}
    	return 0;
    }
    
  • 相关阅读:
    Android 使用系统签名打包apk
    创业公司如何巧用工具提高团队生产力——豌豆荚创始人王俊煜讲述团队背后的“利器”
    Spring整合BoneCP+Hibernate配置数据连接池
    EhCache集群方案JGroups
    IOS 目录结构
    关于hibernate的缓存使用
    用webbrowser控件做一个资源管理器,如何得到IE控件中选中(鼠标多选)的文件名称列表?
    轮回!
    转载:谨以此文献给才毕业25年的朋友
    新的开始
  • 原文地址:https://www.cnblogs.com/stoorz/p/15177750.html
Copyright © 2011-2022 走看看