• Luogu P3403 跳楼机|同余最短路


    题意:给出跳楼机的4个操作,分别为
    1.向上移动(x)层;

    2.向上移动(y)层;

    3.向上移动(z)层;

    4.回到第一层。 显然,并不需要

    求从第一层开始,能到达(1)(h)中的多少层?

    (1<=h<=2^{63}-1)

    (1<=x, y, z<=100000)

    题解:

    好像可以直接(DP)?

    布星啊,看下数据范围。

    那先来推推定理?

    接下来假设(xle yle z)

    对于一个数(k),若它能到达,则(k+x,k+2x,k+...)(均(le h))均能到达,(y)(z)同理

    那我们可以选(x)为模数,若(k)可以,上面的都可以!显然可以设(f[i])表示最小的可以用(y,z)表示出来的数。计算答案只需求(sumlimits^{x-1}_{i=0}(h-f[i])/x+1),空间是质的飞跃

    等等,你这根本不是递推啊,dp个鬼啊。

    好吧,确实不行,但看下转移?

    (f[i+y]=f[i]+y,f[i+z]=f[i]+z)

    似曾相识有没有?

    这与最短路非常相像完 全 一 致!

    那就用最短路代替,从(i->(i+y)\%x)边权(y),从(i->(i+z)\%x)边权(z)

    自此,算法成型。

    这就是同余系最短路

    tips:应从(1\%x)开始跑最短路!

    #include<bits/stdc++.h>
    using namespace std;
    long long cc,to[300100],net[300100],fr[300100],l[300100],g;
    long long ans,hh,f[300100],h[300100],ha[300100],x[4];
    bool vis[300100];
    void addedge(long long u,long long v,long long len)
    {
    	cc++;
    	to[cc]=v;net[cc]=fr[u];fr[u]=cc;l[cc]=len;
    }
    void add(long long x,long long y)
    {
    	g++;
    	h[g]=x;ha[g]=y;
    	long long fa=g/2,so=g;
    	while (h[fa]>h[so]&&fa)
    	{
    		swap(h[fa],h[so]);
    		swap(ha[fa],ha[so]);
    		so=fa;fa/=2;
    	}
    }
    long long del()
    {
    	long long re=ha[1];
    	h[1]=h[g];ha[1]=ha[g];g--;
    	long long fa=1,so=2;
    	if (h[so]>h[so+1]&&so+1<=g) so++;
    	while (h[fa]>h[so]&&so<=g)
    	{
    		swap(h[fa],h[so]);
    		swap(ha[fa],ha[so]);
    		fa=so;so*=2;
    		if (h[so]>h[so+1]&&so+1<=g) so++;
    	}
    	return re;
    }
    void dij()
    {
    	for (long long i=0;i<x[0];i++)
    	  vis[i]=false,f[i]=9223372036854775807;
    	f[1%x[0]]=1;
    	add(1,1%x[0]);
    	while (g)
    	{
    		long long x=del();
    		if (vis[x]) continue;
    		vis[x]=true;
    		for (long long i=fr[x];i;i=net[i])
    		{
    			if (f[to[i]]>f[x]+l[i])
    			{
    				f[to[i]]=f[x]+l[i];
    				add(f[to[i]],to[i]);
    			}
    		}
    	}
    }
    int main()
    {
    	cin>>hh;
    	cin>>x[0]>>x[1]>>x[2];
    	sort(x+0,x+3);
    	for (long long i=0;i<x[0];i++)
    	{
    		addedge(i,(i+x[1])%x[0],x[1]);
    		addedge(i,(i+x[2])%x[0],x[2]);
    	}
    	dij();
    	for (long long i=0;i<x[0];i++)
    	{
    		if (f[i]>hh) continue;
    		ans+=(hh-f[i])/x[0]+1;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

    习题[国家集训队]墨墨的等式 BZOJ链接


    参考资料
    【1】https://www.luogu.org/problemnew/solution/P3403

  • 相关阅读:
    js中有趣的闭包(closure)
    js常见函数汇总
    js时间处理
    一些JavaScript的技巧、秘诀和最佳实践
    js创建对象的6种方式
    js数组常用方法汇总
    左右点击分页方法
    dedecms 添加自定义图字段,调用时出错
    js判断PC端与移动端跳转
    php验证码
  • 原文地址:https://www.cnblogs.com/fmj123/p/Luogu3403.html
Copyright © 2020-2023  润新知