迷宫

有趣的题目。

实际上题目就是让我们构造一个自动机让节点数最小。

有一个简单的构造:构造k个点,点i表示现在%k=i,点i向im+j(0<=j<m)连边,意思是前面的部分*=m,后面的加上当前这一位。

这个构造的点数不是最小的。有一个定理可以用来求出最简自动机:

定义2个节点a,b等价为:这两个节点中不能一个包含终态,一个不包含。若转移函数是f,则对于所有c,f(a,c)=f(b,c)。用并查集可以求出答案获得30分。

考虑每次合并的时候在干什么。实际上,im=jm(%k)的2个点可以被合并,因为他们的转移是相同的。

设f(i)表示i接受的最短串为多少,g(i)表示i*m^f(i)%k,则两个节点i,j当f(i)=f(j)且g(i)=g(j)可以合并。

如果i*m^k=j*m^k,则走长度为k的串后,i接受的串集=j接受的串集。

但是如果一个点能够走<k到达终点,则走的前面的串可能有一个节点可以接受而另一个不行的,不符合要求。

通过这个条件,我们可以得到一个算法:每次把所有节点*k(这下节点i的值就是i*m^k),合并权值相同的点(这些点符合上面的合并要求)。

然后删除序列末尾>i-m^k+1的点,每个点累加答案。

原因是因为这些点在经过k次以后,有一条路径会经过0点,不符合条件,要全部删除。

可以获得50分。

接下来的素数的点可以推一下拿到另外10分。

貌似有类欧拿到80分的做法,以后再学。

满分的测试点需要更快的模拟这个过程。

设d=gcd(k,m)

若d=1,则每个节点的出边集都是不同的,无法合并。

把所有点*=m%=k后,所有点都是d的倍数。

若t=k/d,由数论知识可得1~k-1 *m%k的结果的循环节为t,d的所有倍数都会出现。

前面的过程要求把*m%k相同的数合并。实际上可以合并(k-1)-t次

现在还有t个节点。设k'=k/d,m'=m/d。由于d的倍数在集合内都出现了,所以后面的m'个数都要被删除。剩下的数是1,2,....k'-m'

如果输入的数是n,k,要对节点1,2,....v去重。

初始,v=k-1,m=1,k=k

设d=gcd(k,m),t=k/d

如果d=1,根据之前的分析无法合并

若v<=t,则循环节未完全出现,乘以m得到的结果互不相同,无法合并

若v>t,则所有节点可以被合并,合并完后还有t个点,则m'=m/(n/d)

后面的m个点都被删除,所以新v=t-m',k'=t

把所有数都除以d,递归到下一个子问题。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int t,n,k;
int gcd(int x,int y){
    return y?gcd(y,x%y):x;
}
signed main(){
    scanf("%lld",&t);
    while(t--){
        scanf("%lld%lld",&n,&k);
        int v=k-1,r=k,m=1;
        while(v>0){
            int d=__gcd(k,n),t=k/d;
            if(d==1||v<=t)break;
            r-=v-t;
            if(!(t/m/(n/d)))break;
            m*=n/d;
            v=t-m;
            k/=d;
        }
        printf("%lld
",r);
    }
}
View Code
原文地址:https://www.cnblogs.com/cszmc2004/p/12957426.html