Loading web-font TeX/Math/Italic


UOJ#375. 【ZJOI2018】迷宫

原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ375.html

题解

首先,我们可以建出一个 k 个点的自动机,第 i 个点表示当前数对 k 取模为 i-1。显然每一个点有 m-1 条出边。

然后,稍加观察,我们就可以发现,如果一些节点的出边集合是相同的,我们就可以将他们合并。

具体是怎样的节点呢?

对于每一个 t ,满足

imt(modk)

的所有 i 都可以合并(节点 0 除外)。

然后你就发现你几乎过了大样例的前 100 个数据。

只有很少是错的。

所以大概会得到0分的好成绩。

那么错在了哪儿?

并不是这样合并完了就没有节点可以合并了。

我们再想想哪些节点可以合并。

注意到如果 im2jm2(modk) 的话,只要在后面接上两个字母,ij 就没区别了。

但是如果其中一个走一步就可以到 0 了,另一个不行,那么他们就是不等价的。

除此之外的情况都可以合并。

类似地,对于 m3,m4,m5, 也差不多。

于是我们就可以得到一个 O(klog2k) 的模拟。

可以获得 50 分的好成绩。

再看看质数的情况,由于当 gcd(k,m)=1 时,答案等于 k ,所以 k 一定是 m 的倍数。稍微模拟一下可以发现,一次合并之后相当于缩小了问题规模,而去求 k=k/m,m=m 的子问题。简单推导一下就可以得到质数的10分。

现在已经得到了 60 分的好成绩。

现在我们来看看 60 分的做法给了我们什么启发:缩小问题规模。

d=gcd(k,m)>1 ,则一开始给 1k1 (0是特殊点被ban掉了)乘了 m 模了 k 之后,所有数都是 d 的倍数。

t=kd ,则 1k1 乘m模k 后的结果是 t 个一循环的,所以 d 的每一个倍数都会出现。我们把每一种乘m模k相同的缩起来显然是最优的,那么,我们可以合并 (k1)t 次。

现在还剩下 t 个节点,注意到现在加上 0m1 之后与 k 同余的节点显然不能参与下一次合并,所以我们要把他们扔掉。

扔掉的是那些呢?

好像有点棘手。我们先绕个弯。之前说道这里的所有数都是 d 的倍数。所以我们把所有值都除以 d,k' = k/d, m'=m/d 。由于之前提到 " d 的每一个倍数都会出现",所以所有值除以 d 之后,得到的就是 1,2,,k1 。我们扔掉后 m 个数,所以剩下的就是 1,2,,km

我们发现我们得到了一个原问题的子问题。

于是我们可以考虑把这种问题一般化:

假设读入的两个数第一个是 n ,第二个是 k 。

假设当前剩余节点 1,2,ubmn 的若干次幂除以一些 gcd ,k 表示除过一些 gcd 的 k ,在这种状态下,我们来计算最多可以合并多少节点。初始情况下, ub = k - 1, m = 1, k = k 。

d=gcd(k,n),t=k/d

考虑分几种情况讨论:

1.  d = 1: 显然不能合并。

2.  ubt: 乘以 m 之后的得到的结果互不相同,所以也不能合并。

3.  这种情况下可以合并 ubt 次。合并完之后,显然还剩 t 个点。这时候 m=m(n/d),扔掉 m 个点之后还剩 tm 个,所以 ub=tm ,剩下就是 k=t

注意一下运算的时候不要爆 long long 。

于是你就可以得到 100 分的好成绩。

 

代码1 - 60分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define For(i,a,b) for (int i=a;i<=b;i++)
#define Fod(i,b,a) for (int i=b;i>=a;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define _SEED_ ('C'+'L'+'Y'+'A'+'K'+'I'+'O'+'I')
#define outval(x) printf(#x" = %d\n",x)
#define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
#define outtag(x) puts("----------"#x"----------")
using namespace std;
typedef long long LL;
LL read(){
    LL x=0,f=0;
    char ch=getchar();
    while (!isdigit(ch))
        f|=ch=='-',ch=getchar();
    while (isdigit(ch))
        x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}
LL T,n,k;
LL gcd(LL a,LL b){
    return b?gcd(b,a%b):a;
}
vector <int> v;
int tn(int x){
    return x?x:k;
}
int main(){
    T=read();
    while (T--){
        n=read(),k=read();
        if (gcd(n,k)==1){
            printf("%lld\n",k);
            continue;
        }
        if (n>1e5||k>1e5){
            LL ans=k,c=0;
            while (ans%n==0)
                ans/=n,c++;
            printf("%lld\n",ans+c);
            continue;
        }
        LL m=1,ans=k;
        v.clear();
        For(i,0,k-1)
            v.pb(tn(i));
        while (1){
            sort(v.begin(),v.end());
//          outvec(v);
            if (v.empty())
                break;
            int cnt=unique(v.begin(),v.end())-v.begin();
            while (v.size()>cnt)
                v.pop_back(),ans--;
//          outvec(v);
            while (!v.empty()&&v.back()>k-m)
                v.pop_back();
//          outvec(v);
            for (auto &i : v)
                i=tn(i*n%k);
            if (k/m==0)
                break;
            m*=n;
        }
        cout<<ans<<endl;
    }
    return 0;
}

  

代码2 - 100分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define For(i,a,b) for (int i=a;i<=b;i++)
#define Fod(i,b,a) for (int i=b;i>=a;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define _SEED_ ('C'+'L'+'Y'+'A'+'K'+'I'+'O'+'I')
#define outval(x) printf(#x" = %d\n",x)
#define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
#define outtag(x) puts("----------"#x"----------")
using namespace std;
typedef long long LL;
LL read(){
    LL x=0,f=0;
    char ch=getchar();
    while (!isdigit(ch))
        f|=ch=='-',ch=getchar();
    while (isdigit(ch))
        x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}
LL T,n,k;
LL gcd(LL a,LL b){
    return b?gcd(b,a%b):a;
}
int main(){
    T=read();
    while (T--){
        n=read(),k=read();
        LL ans=k,ub=k-1,m=1;
        while (ub>0){
            LL d=gcd(k,n),t=k/d;
            if (d==1||ub<=t)
                break;
            ans-=ub-t;
            if (t/m/(n/d)==0)
                break;
            m*=n/d;
            ub=t-m;
            k/=d;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

  

posted @   zzd233  阅读(591)  评论(0编辑  收藏  举报
编辑推荐:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
阅读排行:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!

点击右上角即可分享
微信分享提示