• 【hdu 6071】Lazy Running


    菜鸡永远都在做着变聚的梦。

    题意

      有 (4) 个点连成一个环,连接顺序依次为 (1-2-3-4-1)。相邻两个点之间有个距离 (d_{i,i+1})(特别地,当 (i=4) 时为 (d_{4,1}))。
      有一个人在 (2) 号点,他要完成距离为 (K) 的跑步任务并回到 (2) 号点,问至少需要跑多长距离。
      (1le d_ile 30000,space 1le Kle 10^{18})

    题解

      剩余系裸题?墨墨的等式练习题?
      考场上瞎写了一波就交了,考后发现转移情况想 sb 了炸成 (70) 分了……(啪)

      

      首先你需要会解决这么一个经典问题:给你 (n(nle 20)) 个数 (a_1,a_2,...,a_n),求用这些数能加出来的 (ge K(Kle 10^{18})) 的最小值(每个数可以用任意多次),即求 (a_1 x_1 + a_2 x_2 + ... + a_n x_n)([K,infty)) 范围内的最小值。
      这就要用到一个东西:剩余系(我也忘了到底叫啥了,也可能是同余系)
      虽然 (K) 的范围很大,但注意到 (a_i) 的范围并不大,所以我们任取一个数 (a_p)(这里为了方便设 (p)(n)),以此为模数建系。
      或者说,将 ([0,infty]) 内的正整数按照模 (a_p) 的值分类,所有模 (a_n) 的值相同的分入一类。显然我们只需要求出同一类数中最小的能加出来的数(设其为 (Min)),因为在这一类中,大于等于 (Min) 的所有数显然都能加出来(把 (Min) 加上若干个 (a_n) 即可),所以并不用考虑它们能不能被其它 (a_i) 加出来。
      那怎么求每一类中最小的能加出来的数呢?
      把 ([0,a_p-1]) 中的每个正整数视为一个点,每个点有 (n-1) 条出边,(i) 号点的第 (j) 条出边指向 ((i+a_j)\% a_n) 号点,边权为 (a_j),然后从 (0) 号点出发跑单源最短路即可。
      首先时间复杂度肯定是对的,点数为 (a_n),边数为 (a_n imes (n-1)),跑 ( ext{dij}) 复杂度稳定 (O(a_nlog a_n))
      然后考虑正确性。因为边权是 (a_i),所以到 (x) 号点的最短路就是 (K)(x) 的一类中 最小的一个能被一堆 (a_i) 加出来的数。这种做法用最短路算法保证了时间复杂度的正确性……好神仙啊……

      

      然后考虑本题。我们只需要随便选取一个大小合适的数 作为剩余系的模数就行了。
      不难发现 (w=d_{1,2}) 就很合适,(2w) 代表从 (2) 号点跑到 (1) 号点再跑回来。
      可以直接以 (d_{1,2}) 为模数建剩余系,那么设 (dis_{i,j}) 表示从起点出发到达 (i) 号点,距离模 (2w)(j) 时的最短路。
      (8w) 个状态跑 ( ext{dij}),复杂度 (O(wlog w))

      我 tm 忘了剩余系的模数可以取最小值,之前一直在取最大值

    #include<bits/stdc++.h>
    #define ll long long
    #define N 100010
    #define pii pair<int,int>
    #define mp make_pair
    #define fi first
    #define se second
    const ll ll_inf = 9223372036854775807ll >> 1;
    using namespace std;
    inline ll read(){
    	ll x=0; bool f=1; char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
    	for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
    	if(f) return x; return 0-x;
    }
    ll k;
    int w,d[5];
    ll dis[5][N];
    bool vis[5][N];
    priority_queue<pair<ll,pii>, vector<pair<ll,pii> >, greater<pair<ll,pii> > > Q;
    void Dij(){
    	for(int i=0; i<4; ++i)
    		for(int j=0; j<w; ++j)
    			dis[i][j]=ll_inf, vis[i][j]=0;
    	dis[1][0]=0;
    	Q.push(mp(0,mp(1,0)));
    	pii tmp; int x,y;
    	while(!Q.empty()){
    		//int faq=Q.top().fi;
    		tmp=Q.top().se; Q.pop();
    		x=tmp.fi, y=tmp.se;
    		if(vis[x][y]) continue;
    		//cout<<faq<<' '<<x<<' '<<y<<endl;
    		vis[x][y]=1;
    		int x1=(x+1)%4, x2=(x+3)%4;
    		int y1=(y+d[x])%w, y2=(y+d[x2])%w;
    		if(dis[x1][y1]>dis[x][y]+d[x]){
    			dis[x1][y1]=dis[x][y]+d[x];
    			Q.push(mp(dis[x1][y1],mp(x1,y1)));
    		}
    		if(dis[x2][y2]>dis[x][y]+d[x2]){
    			dis[x2][y2]=dis[x][y]+d[x2];
    			Q.push(mp(dis[x2][y2],mp(x2,y2)));
    		}
    	}
    }
    int main(){
    	int T=read();
    	while(T--){
    		k=read();
    		for(int i=0; i<4; ++i) d[i]=read();
    		w=min(d[0]<<1,d[1]<<1);
    		Dij();
    		//cout<<dis[1][2165%1200]<<endl;
    		while(dis[1][k%w]>k) ++k;
    		printf("%lld
    ",k);
    	}
    	return 0;
    }
    

      scb 神仙推出了个不同的做法(但还是以剩余系为基础),需要对 (4) 个式子各求一遍经典问题,但是跑得很快 Orz 大家有兴趣可以去学(mo)一下

  • 相关阅读:
    linux centos 常用命令(需掌握)
    centos轻松搭建NFS
    Centos6.1在yum安装软件的时候,居然报错了,如何解决
    安装好Centos后,需要做的几件事情。
    使用scp命令传输文件
    批量删除文件或者批量修改文件
    Centos7搭建常用的LNMP架构
    python实现自动抠名字签名,比PS还快
    inotify软件实现实时同步,ssh-key 秘钥连接方式,saltstack实战批量管理Linux,expect批量分发秘钥
    Cisco 路由器配置OSPF 动态路由 (开放式最短路径优先)
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/hdu6071.html
Copyright © 2020-2023  润新知