zoukankan      html  css  js  c++  java
  • 4.10 模拟赛 最小生成树 克鲁斯卡尔思想 优化建图

    avatar
    avatar

    这道题超级的妙。

    30分可以发现题目中的建图最多建n条 剩下的都没用 所以暴力建nQ条边跑克鲁斯卡尔即可。

    对于Q<=50 发现此时边数为为2e7 跑克鲁斯卡尔必然GG.

    考虑prim求最小生成树 复杂度nlogn+2e7.

    这样就可以暴力得到50分了。

    最后考虑一下(ai-bi)<=1的情况。

    可以发现此时连边有特点 对于ai<bi 连边是 2,1 3,2 4,3....f[i][0]表示i这个点和其父亲连的边权即可。

    对于ai==bi 和上面情况几乎一样。

    对于ai>bi 其中一种情况和上面一样 还会发现有 4,2 5,3 6,4这种连边方式 f[i][1]表示i这个点和其父亲的父亲的连的边权即可。

    利用f数组进行更新其他的f值 可以发现存在环 所以dp两次即可 我当时傻了 直接开堆放f数组 最小的来更新了 也行。

    然后再做最小生成树 复杂度nlogn.

    80分代码:

    const int maxn=200010;
    int n,flag,Q,vis[maxn];
    vector<pii>g[maxn];
    struct wy{int x,y,z;}t[maxn];
    int d[maxn];ll sum;
    priority_queue<pii>q;
    int f[maxn][2];
    inline void add(int x,int y,int z)
    {
    	if(z>=INF)return;
    	if(x==y)return;
    	g[x].pb(mk(y,z));
    	g[y].pb(mk(x,z));
    }
    inline void prim()
    {
    	rep(1,n,i)d[i]=INF,vis[i]=0;
    	d[1]=0;q.push(mk(-d[1],1));
    	while(q.size())
    	{
    		int x=q.top().S;q.pop();
    		if(vis[x])continue;
    		vis[x]=1;sum+=d[x];
    		for(int j=0;j<g[x].size();++j)
    		{
    			int tn=g[x][j].F,e=g[x][j].S;
    			if(d[tn]>e)
    			{
    				d[tn]=e;
    				q.push(mk(-d[tn],tn));
    			}
    		}
    	}
    }
    int main()
    {
    	freopen("spanning.in","r",stdin);
    	freopen("spanning.out","w",stdout);
    	get(n);get(Q);
    	rep(1,Q,i)
    	{
    		int x,y,z;
    		get(x);get(y);get(z);
    		t[i]=(wy){x,y,z};
    		if(abs(x-y)>1)flag=1;
    	}
    	if(!flag)
    	{
    		rep(0,n-1,i)f[i][0]=f[i][1]=INF;
    		rep(1,Q,i)
    		{
    			int x,y,z;
    			x=t[i].x;y=t[i].y;z=t[i].z;
    			if(x==y){f[(x+1)%n][0]=min(f[(x+1)%n][0],z+1);continue;}
    			if(x<y)f[y][0]=min(f[y][0],z);
    			else
    			{
    				f[x][0]=min(f[x][0],z);
    				f[(x+1)%n][1]=min(f[(x+1)%n][1],z+1);
    			}
    		}
    		rep(0,n-1,i)
    		{
    			if(f[i][1]!=INF)q.push(mk(-f[i][1],i));
    			if(f[i][0]!=INF)q.push(mk(-f[i][0],i));
    		}
    		while(q.size())
    		{
    			int x=q.top().S;
    			int ww=-q.top().F;
    			q.pop();
    			if(ww==f[x][0])
    			{
    				int tn=(x+1)%n;
    				if(f[tn][0]>f[x][0]+2)
    				{
    					f[tn][0]=f[x][0]+2;
    					q.push(mk(-f[tn][0],tn));
    				}
    			}
    			if(ww==f[x][1])
    			{
    				int tn=(x+1)%n;
    				if(f[tn][1]>f[x][1]+2)
    				{
    					f[tn][1]=f[x][1]+2;
    					q.push(mk(-f[tn][1],tn));
    				}
    			}
    		}
    		rep(1,n-1,i)
    		{
    			add(i+1,i,f[i][0]);
    			if(i>=2)add(i+1,i-1,f[i][1]);
    		}
    		add(1,n,f[0][0]);
    		add(1,n-1,f[0][1]);
    		add(2,n,f[1][1]);
    		prim();putl(sum);
    		return 0;
    	}
    	if(flag)
    	{
    		if((ll)n*Q<=10000000)
    		{
    			rep(1,Q,i)
    			{
    				int x=t[i].x;int y=t[i].y;int z=t[i].z;
    				int s1=0,s2=0;add(x+1,y+1,z);
    				rep(1,n*2-1,j)
    				{
    					if(j&1)++s1;else ++s2;
    					add((s1+x)%n+1,(s2+y)%n+1,z+j);
    				}
    			}
    			sum=0;prim();putl(sum);
    		}
    	}
    	return 0;
    }
    

    考虑100分 我们还是可以发现这是一个优化建图的问题。

    考虑克鲁斯卡尔的过程 对于边a,b,c a向b连了一条c的边 对于下一条边 a+1,b,c+1 对于这条边我们可以设想一下 在跑克鲁斯卡尔的时候 由于上一条边比当前边权小 上一条边被便利的时候 a,b必然形成了一个联通快。

    此时 对于a+1,b,c+1这条边就等价于 a+1,a,c+1 而下条边 a+1,b+1,c+2由于同样的原因可以转换为 b,b+1,c+2.

    设f[i]表示i点和其父亲连边的边权最小值 dp一下即可 可以发现这样做所有边都被简化了。

    然后跑克鲁斯卡尔即可 总边数Q+n.复杂度(Q+n)log 。

    const int MAXN=400010;
    int n,cnt,Q;ll sum;
    struct wy
    {
    	int x,y,z;
    }t[MAXN<<1];
    int f[MAXN];
    inline int cmp(wy a,wy b){return a.z<b.z;}
    inline int getfather(int x){return x==f[x]?x:f[x]=getfather(f[x]);}
    int main()
    {
    	freopen("spanning.in","r",stdin);
    	freopen("spanning.out","w",stdout);
    	memset(f,0x3f,sizeof(f));
    	get(n);get(Q);
    	rep(1,Q,i)
    	{
    		int x,y,z;
    		get(x);get(y);get(z);
    		t[++cnt]=(wy){x+1,y+1,z};
    		f[(x+1)%n]=min(f[(x+1)%n],z+1);
    		f[(y+1)%n]=min(f[(y+1)%n],z+2);
    	}
    	rep(1,n-1,i)f[i]=min(f[i-1]+2,f[i]);
    	f[0]=min(f[n-1]+2,f[0]);
    	rep(1,n-1,i)f[i]=min(f[i-1]+2,f[i]);
    	rep(1,n-1,i)t[++cnt]=(wy){i,i+1,f[i]};
    	t[++cnt]=(wy){1,n,f[0]};
    	rep(1,n,i)f[i]=i;
    	sort(t+1,t+1+cnt,cmp);
    	rep(1,cnt,i)
    	{
    		int xx=getfather(t[i].x);
    		int yy=getfather(t[i].y);
    		if(xx==yy)continue;
    		f[xx]=yy;sum+=t[i].z;
    	}
    	putl(sum);
    	return 0;
    }
    
  • 相关阅读:
    Bundle 机制
    三次握手和四次挥手
    SparseArray
    ThreadLocal ——android消息机制handler在非主线程创建not called Looper.prepare() 错误的原因
    怎么去除重复代码
    ClassLoader
    android的四种线程池
    LeetCode#50 Pow(x, n)
    LeetCode#49 Anagrams
    LeetCode#48 Rotate Image
  • 原文地址:https://www.cnblogs.com/chdy/p/12681854.html
Copyright © 2011-2022 走看看