• 【洛谷4339】[ZJOI2018] 迷宫(神仙题)


    点此看题面

    • 有一座(n)个点(标号为(0sim n-1))的迷宫,每个点各自连出(m)条边(可有重边和自环,标号分别为(0sim m-1))。
    • (0)号点出发,把每次选择的边的编号记录下来得到一个(m)进制数。
    • 给定(m,k),求最小的(n)使得能构造出一个合法的迷宫,使得一条路径最终走回(0)号点当且仅当它对应的(m)进制数是(k)的倍数。
    • (m,kle10^{18})

    (n=k)的构造方案

    考虑对于点(x),令其第(i)条边连向((xcdot m+i)\%k),这样一来一条路径走到的点的编号,就等于其对应(m)进制数模(k)的余数。

    那么一条路径走到(0)号点,等价于对应(m)进制数模(k)(0),也就是(k)的倍数,符合题意。

    这个构造完美利用了题目给出的条件,在点的编号和路径的(m)进制数间建立起了直接联系,说实话已经算是一步挺妙的转化了。

    缩点获取最优方案

    两个点能缩在一起,当且仅当这两个点是等价的,也就是说这两个点每条边指向的点必须相同。

    容易发现一次缩点之后可能使得一些新的点变得等价,但方便起见我们先讨论第一次缩点。

    两个点(x,y)缩点的充要条件是(forall iin[0,m),xm+iequiv ym+i(mod k)),但这个(i)显然没啥意义,两边可以同时减去,因此实际上就是要满足(xmequiv ym(mod k))

    (g=gcd(m,k)),并设(m'=frac mg,k'=frac kg)

    则原式可以化作(xm'equiv ym'(mod k')),即(xequiv y(mod k'))

    一个点的编号可以表示为(u imes k'+v),那么根据我们得出的结论,缩点的充要条件是(v)相等。

    不同的(v)总共有(k')个,因此能缩的点数就应该是((k-1)-k')(要减(1)是因为(0)号点作为一个特殊点无法缩去)。

    递归缩点

    之前就提到了,一次缩点之后,因为一些原本不相同的点变得相同了,所以可能会使得一些新的点变得等价,因此这里的缩点需要递归求解。

    此时发现(x,y)能被缩点等价于 (xcdot m^dequiv ycdot m^d(mod k))。我们仍想化出先前(xequiv y(mod k'))这样的等价式子,就发现(k')相当于是(k)进行了(d)次除以与(m)(gcd)的操作,递归的过程中可以直接记录,并同时用一个变量(t)记录每次(m')的总乘积。

    再开一个变量(r)记录剩余的点数,那么这次缩去的点数就应该是(r-k')(因为缩点的时候每种不同的余数都必然有点剩余,可以保证一定存在所有(k')种余数的点)。而这次缩点后就应该有(t)个点无法再被缩,新的(r)就应该是(k'-t)

    综上,递归函数只需要记录三个变量(r,t,k),分别记录剩余点数、(m')总乘积、(d-1)次约分后的(k)

    递归具体细节:边界

    显然如果(g=1)肯定无法缩点。而若(r<k‘),由于点数小于出边构成集合的种类数,也无法再缩点。

    如果某一时刻(t>k'),则下次递归的(r=k'-t<0)肯定不行,因此我们递归前要先比较(k')(t)的大小。又因为(t)乘上当前的(m')之后可能会爆(long long),所以我们要在更新(t)之前把(t imes m')(long double)(k')比较,这样就没问题了。

    代码:(O(nlog^2k))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define LL long long
    using namespace std;
    LL m,k;I LL gcd(Con LL& x,Con LL& y) {return y?gcd(y,x%y):x;}
    I LL Solve(Con LL& r,Reg LL t,Con LL& k)//递归缩点
    {
    	LL g=gcd(m,k),k_=k/g,m_=m/g;if(g==1||r<k_) return 0;//g=1或r<k'则无法缩点
    	return (1.0L*t*m_<=k_?(t*=m_,Solve(k_-t,t,k_)):0)+r-k_;//t>k'无须递归,此次缩去r-k'个点
    }
    int main()
    {
    	RI Tt;scanf("%d",&Tt);W(Tt--) scanf("%lld%lld",&m,&k),printf("%lld
    ",k-Solve(k-1,1,k));return 0;//总点数-缩去点数
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    Oracle ORA-01033: ORACLE initialization or shutdown in progress 错误解决办法
    Oracle用imp导入dmp 提示遇到 ORACLE 错误 12560 TNS: 协议适配器错误 解决方法
    Oracle恢复误删除表操作语句
    DevExpress GridControl使用方法总结
    PL/SQL Developer 中的问题:Initialization error Could not load ".../oci.dll"解决方法
    Oracle中查询当前数据库中的所有表空间和对应的数据文件语句命令
    [PHP]利用XAMPP搭建本地服务器, 然后利用iOS客户端上传数据到本地服务器中(三. PHP端代码实现)
    [iOS]ios archives 出现的是other items而不是iOS Apps的解决方案
    [PHP]利用XAMPP搭建本地服务器, 然后利用iOS客户端上传数据到本地服务器中(二.配置MySQL数据库)
    [软件]XAMPP错误解决
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4339.html
Copyright © 2020-2023  润新知