zoukankan      html  css  js  c++  java
  • 【GMOJ3860】地壳运动

    题目

    题目链接:https://gmoj.net/senior/#main/show/3860
    \(n\)个点,\(m\)条边,每一条边可以用\((x,y,u,v)\)表示,表示一条连接\(x,y\)的边的长度为\(k_1x+k_2y\)。接下来\(q\)组询问,每次询问给出\(k_1,k_2\),求此时的最小生成树。
    \(n\leq 35,m\leq 25000,q\leq 200000\)

    思路

    写的是暴力\(O(m^2+qn^2)\)的算法。鉴于这道题时限\(5s\)\(GMOJ\)很快,而且复杂度远远跑不满,这样的暴力算法是可以过的。
    先把一条边的长度\(=k_1x+k_2y\)全部除以\(k_1\),变成\(x+\frac{k_2}{k_1}y\)。显然这样两条边之间的大小关系不变。
    我们考虑连接两个点\(x,y\)的所有边,求出\(k1,k2\)在什么区间内时满足这条边是连接\(x,y\)的所有边中最小的。
    如果两条边\((x,y,u_1,v_1)(x,y,u_2,v_2)\)满足前者比后者短,那么

    • 如果\(u_1\leq u_2\)\(v_1\leq v_2\),那么\(k_1,k_2\)取任意正数均成立。
    • 如果\(u_1<u_2,v_1>v_2\),那么有\(u_1+v_1\frac{k_2}{k_1}<u_2+v_2\frac{k_2}{k_1}\),即\(\frac{k_2}{k_1}<\frac{u_1-u_2}{v_2-v_1}\)
    • 如果\(u_1>u_2,v_1<v_2\),那么同理有\(\frac{k_2}{k_1}>\frac{u_1-u_2}{v_2-v_1}\)
      那么这样就可以在均摊\(O(m^2)\)的时间复杂度内求出\(k1,k2\)在什么区间内时满足这条边是连接\(x,y\)的所有边中最小的。

    接下来我们把询问按\(\frac{k_2}{k_1}\)从大到小排序,然后枚举两个点\(x,y\),利用上文求出来的对应区间,指针扫描出最短的路,然后跑\(Prim\)即可。
    千万注意求最小生成树不能使用\(Kruskal\),因为\(Kruskal\)需要将道路长度排序,这样就多了一个\(log\ (n^2)\)的复杂度,使用\(Prim\)可以稳定\(O(n^2)\)
    时间复杂度\(O(m^2+qn^2)\)

    代码

    #include <cstdio>
    #include <cctype>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N=41,M=25011,Q=200011;
    int n,m,T,tot,pos[N][N],father[N],head[N][N],maxxx;
    double k1,k2,ans[Q],dis[N][N];
    bool vis[N];
    
    inline int read()
    {
    	int d=0; char ch=getchar();
    	while (!isdigit(ch)) ch=getchar();
    	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
    	return d;
    }
    
    struct node
    {
    	double u,v,l,r;
    	int next;
    }road[M]; 
    
    struct Query
    {
    	int id;
    	double k,k2,k1;
    }ask[Q];
    
    struct edge
    {
    	int from,to,u,v;
    	double minn,maxn;
    }e[M];
    
    inline bool cmp1(edge x,edge y)
    {
    	if (x.from<y.from) return 1;
    	if (x.from>y.from) return 0;
    	return x.to<y.to;
    }
    
    inline bool cmp2(Query x,Query y)
    {
    	return x.k<y.k;
    }
    
    inline int find(int x)
    {
    	return x==father[x]?x:father[x]=find(father[x]);
    }
    
    inline double mst()
    {
    	double sum=0.0;
    	for (int i=1;i<=n;i++)
    		dis[0][i]=dis[1][i],vis[i]=0;
    	dis[0][0]=1000000000000.0; vis[1]=1;
    	for (int j=1;j<n;j++)
    	{
    		int Pos=0;
    		for (int i=2;i<=n;i++)
    			if (!vis[i] && dis[0][i]<dis[0][Pos]) Pos=i;
    		vis[Pos]=1; sum+=dis[0][Pos];
    		for (int i=1;i<=n;i++) 
    			if (!vis[i]) dis[0][i]=min(dis[0][i],dis[Pos][i]);
    	}
    	return sum;
    }
    
    int main()
    {
    	n=read(); m=read(); T=read();
    	for (register int i=1;i<=m;++i)
    	{
    		int x=read(),y=read(),u=read(),v=read();
    		if (x>y) swap(x,y);
    		e[i].from=x; e[i].to=y; e[i].u=u; e[i].v=v;
    		e[i].maxn=1000000000000.0; e[i].minn=0.0; 
    	}
    	sort(e+1,e+1+m,cmp1);
    	memset(head,-1,sizeof(head));
    	for (register int l=1,r=1;r<=m;l=r=r+1)
    	{
    		while (e[r+1].from==e[l].from && e[r+1].to==e[l].to) r++;
    		for (register int i=l;i<=r;++i)
    			for (register int j=i+1;j<=r;++j)
    			{
    				if (e[i].u<=e[j].u && e[i].v<e[j].v)
    					e[j].minn=1000000000000.0,e[j].maxn=0.0;
    				else if (e[i].u>=e[j].u && e[i].v>e[j].v)
    					e[i].minn=1000000000000.0,e[i].maxn=0.0;
    				else if (e[i].v<=e[j].v && e[i].u<e[j].u)
    					e[j].minn=1000000000000.0,e[j].maxn=0.0;
    				else if (e[i].v>=e[j].v && e[i].u>e[j].u)
    					e[i].minn=1000000000000.0,e[i].maxn=0.0;
    				else if (e[i].u<e[j].u)
    				{
    					e[i].maxn=min(e[i].maxn,1.0*(e[i].u-e[j].u)/(e[j].v-e[i].v));
    					e[j].minn=max(e[j].minn,1.0*(e[i].u-e[j].u)/(e[j].v-e[i].v));
    				}
    				else if (e[i].u>e[j].u)
    				{
    					e[i].minn=max(e[i].minn,1.0*(e[i].u-e[j].u)/(e[j].v-e[i].v));
    					e[j].maxn=min(e[j].maxn,1.0*(e[i].u-e[j].u)/(e[j].v-e[i].v));
    				}
    			}
    		for (register int i=l;i<=r;++i)
    			for (register int j=i+1;j<=r;++j)
    				if (e[i].minn<e[j].minn) swap(e[i],e[j]);
    		for (register int i=l;i<=r;++i)
    			if (e[i].maxn>=e[i].minn)
    			{
    				road[++tot].l=e[i].minn; road[tot].r=e[i].maxn;
    				road[tot].u=e[i].u; road[tot].v=e[i].v;
    				road[tot].next=head[e[i].from][e[i].to];
    				head[e[i].from][e[i].to]=tot;
    			}
    	}
    	for (int i=1;i<=n;i++)
    		for (int j=i+1;j<=n;j++)
    			pos[i][j]=head[i][j];
    	for (register int i=1;i<=T;++i)
    	{
    		scanf("%lf%lf",&k1,&k2);
    		ask[i].k=k2/k1; ask[i].id=i;
    		ask[i].k1=k1; ask[i].k2=k2;
    	}
    	sort(ask+1,ask+1+T,cmp2);
    	for (register int k=1;k<=T;k++)
    	{
    		for (int i=1;i<=n;i++)
    			for (int j=1;j<=n;j++)
    				dis[i][j]=1000000000000.0;
    		tot=0; k1=ask[k].k1; k2=ask[k].k2;
    		for (register int i=1;i<=n;++i)
    			for (register int j=i+1;j<=n;++j)
    			{
    				for (;~pos[i][j];pos[i][j]=road[pos[i][j]].next)
    					if (road[pos[i][j]].r>=ask[k].k) break;
    				if (road[pos[i][j]].l<=ask[k].k && road[pos[i][j]].r>=ask[k].k)
    					dis[i][j]=dis[j][i]=road[pos[i][j]].u*k1+road[pos[i][j]].v*k2;
    			}
    		ans[ask[k].id]=mst();
    	}
    	for (register int i=1;i<=T;++i)
    		printf("%0.3lf\n",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    运筹学——线性规划及单纯形法求解
    jQuery实现回到顶部功能
    JS与PHP传递JSON数据
    学习笔记Quartz定时器
    MYSQL常用命令总结
    DWZ框架与第三方jQuery插件整合
    jQuery中增加新元素后没法响应原有的事件
    java.lang.OutOfMemoryError: Java heap space解决方法
    Adobe Edge Animate1.0 使用元件创建嵌套动画
    Adobe Edge Animate 1.0 制作自定义动态菜单
  • 原文地址:https://www.cnblogs.com/stoorz/p/12253990.html
Copyright © 2011-2022 走看看