循环(NOIP2005普及组第四题)

本文探讨了一个有趣的信息学问题:对于一个整数n的正整数次幂,其后k位数是否存在循环现象及循环长度的计算方法。通过对特定案例的分析,提出了一种有效的算法实现思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

循环(circle.pas/circle.in/circle.out)

(NOIP2005普及组T4)
(LYOI20090321信息学综合模拟Problem2)

问题描述
乐乐是一个聪明而又勤奋好学的孩子。他总喜欢探求事物的规律。一天,他突然对数的正整数次幂产生了兴趣。
众所周知,2的正整数次幂最后一位数总是不断的在重复2,4,8,6,2,4,8,6……我们说2的正整数次幂最后一位的循环长度是4(实际上4的倍数都可以说是循环长度,但我们只考虑最小的循环长度)。类似的,其余的数字的正整数次幂最后一位数也有类似的循环现象:
2 2486 4
3 3971 4
4 46 2
5 5 1
6 6 1
7 7931 4
8 8426 4
9 91 2
这时乐乐的问题就出来了:是不是只有最后一位才有这样的循环呢?对于一个整数n的正整数次幂来说,它的后k位是否会发生循环?如果循环的话,循环长度是多少呢?
注意:
1. 如果n的某个正整数次幂的位数不足k,那么不足的高位看做是0。
2. 如果循环长度是L,那么说明对于任意的正整数a,n的a次幂和a + L次幂的最后k位都相同。

样例输入
32 2
样例输出
4
数据规模
对于30%的数据,k <= 4;
对于全部的数据,k <= 100
思路:
这个题我之前是在OJ上做过的,也知道这是NOIP题目,但是一直是0分没有AC。今天终于AC啦。下面讲讲思路:
一开始我的思路就已经有点沾正解的边了,一开始我是认为对于最终的结果SOLVE(N,K),这个数一定是N的最后一位的循环节的倍数,比如说:SOLVE(1234567,7)一定是7的循环节,也就是4的倍数。然后就可以每1234567^4进行一次乘法,不考虑精度的话,本以为这样就能得出30%的答案了,但是只得了2个点。
这里我们记X的循环节为FUNC(X),经过半个下午的思考,发现其实自己只需要把刚才的思路再拓展一下就好了。
比如说以SOLVE(223,3)为例,此时T=FUNC(3)=4模拟一下就是
223^1≡223(mod 1000)
223^2≡729(mod 1000)
223^3≡567(mod 1000)
223^4≡441(mod 1000)
先让它的倍增单位变为[223^FUNC(3)]%(10^3)=441,也就是每次都乘上441,然后就能保证个位每次都是3,记录循环节4,解释一下就是:
223*(441^1)≡343(mod 1000)
223*(441^2)≡263(mod 1000)
223*(441^3)≡983(mod 1000)
223*(441^4)≡503(mod 1000)
223*(441^5)≡823(mod 1000)
这个时候发现,223的十位为2,823的十位也为2,这样每次倍增单位就变成了(441^5)mod 1000=201,然后就能保证十位每次都是2,个位每次都是3。记录循环4*5=20
223*(201^1)≡823(mod 1000)
223*(201^2)≡423(mod 1000)
223*(201^3)≡023(mod 1000)
223*(201^4)≡623(mod 1000)
223*(201^5)≡223(mod 1000)
这个时候发现,223的百位为2,求出的223的百位也为2,这时得到的数已经到了K位了。然后就能保证百位每次都是2,十位每次都是2,个位每次都是3。记录循环4*5*5=100。
然后输出即可。
通过模拟可能思路就比较明晰了。
过程中要用到一个高精乘高精和一个高精乘低精,然后个人认为没必要为了省这点时间去打长长的高精度快速幂。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<set>
using namespace std;
int a[201],k,i,j,t[201];
int shl[10]={1,1,4,4,2,1,1,4,4,2};
string s;
int last[201];
int n[201],ans[201],aans[201],now[201];
int r()
{
    int ans=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        ans*=10;
        ans+=ch-'0';
        ch=getchar();
    }
    return ans*f;
}

void multiply(int x[],int y[],int z[])
{
    int up=0;//进位 
    for(int ii=1;ii<=k;ii++)
    {
    for(j=1;j<=k;j++)
    {
        z[ii+j-1]+=(x[j]*y[ii]+up)%10;
        up=(x[j]*y[ii]+up)/10;
    }
    up=0;}

    for(int ii=1;ii<=k;ii++)
    {
    z[ii+1]+=z[ii]/10;
    z[ii]%=10;
    }
}

void multiply1(int x[],int yy,int z[])
{
    int up=0;//进位 
    for(int ii=1;ii<=k;ii++)
    {
    z[ii]=(x[ii]*yy+up)%10;
    up=(x[ii]*yy+up)/10;
    }
}

int main()
{
//  freopen("circle.in","r",stdin);
//  freopen("circle.out","w",stdout);
    cin>>s;
    k=r();

    int temp=0,len=s.size();
    for(i=len-1;i>=len-k;i--)
    n[++temp]=s[i]-'0';
    for(i=1;i<=k;i++)
    ans[i]=n[i];

    for(i=1;i<shl[n[1]];i++)
    {
        memset(aans,0,sizeof(aans));
        multiply(ans,n,aans);
        for(j=1;j<=k;j++)
        {
            ans[j]=aans[j];
        }
    }

    t[1]=shl[n[1]];

    for(j=1;j<=k;j++)
    now[j]=ans[j];


    int pos=2;
    while(pos<=k)
    {
    for(j=1;j<=k;j++)
    ans[j]=n[j],last[j]=now[j];
    temp=0;
    while(temp<11)
    {
        temp++;

        memset(aans,0,sizeof(aans));
        multiply(ans,now,aans);
        for(j=1;j<=k;j++)
        ans[j]=aans[j];

        if(ans[pos]==n[pos])
        break;

        memset(aans,0,sizeof(aans));
        multiply(last,now,aans);
        for(j=1;j<=k;j++)
        last[j]=aans[j];
    }
    if(temp>=11)
    {
        cout<<-1;return 0;
    }
    for(j=1;j<=k;j++)
    now[j]=last[j];

    memset(aans,0,sizeof(aans));
    multiply1(t,temp,aans);
    for(j=1;j<=100;j++)
    t[j]=aans[j];

    pos++;
    }
    int kk=0;
    for(i=100;i>=1;i--)
    {
    if(t[i])
    kk=1;
    if(kk)
    cout<<t[i];
    }

    return 0;
}

这里写图片描述

<think>嗯,我现在得仔细看看用户提供的代码,找出他在NOIP2013普及组T4这道中的错误。这道应该是一个拓扑排序相关的目,可能涉及到求最长路径或者层级的问,因为目中的ans记录的是最大的层级数,也就是可能需要确定每个节点所在的层,然后找到最大的那个。 首先,用户代码的大致结构是这样的:输入n和m,然后处理m个测试用例。每个用例输入t个点,标记这些点为vis[x]=1,然后对于每个tt[i]中的点,遍历所有未被标记的点j,建立从j到tt[i]的边,同时增加tt[i]的入度。之后进行拓扑排序,计算每个节点的层级,最后输出最大的层级ans。 可能的错误点在哪里呢?我得仔细分析每一步的逻辑是否正确。 首先,目中的问可能类似于“车站划分”的问,每个测试用例可能代表一个车次的站点顺序。比如,每个车次给出t个站点,这些站点必须按顺序到达,也就是说,前面的站点必须比后面的站点层级低。例如,如果一个车次有站点序列a1,a2,a3,那么需要建立a1到a2的边,a2到a3的边,以及所有非该车次的站点到这些站点的边?或者可能用户的理解有误? 但用户代码的处理方式是,对于每个车次的t个站点tt[1..t],然后对于每个tt[i],将所有的未被vis标记的点j(即不在这个车次中的站点)都向tt[i]建立边。这似乎有问。比如,假设车次中的站点是按顺序的,比如S1,S2,S3,那么正确的依赖应该是S1必须在S2之前,S2在S3之前。所以应该建立S1→S2,S2→S3的边,而不是让所有不在车次中的点都指向S1、S2、S3。这可能就是用户代码的错误所在。 比如,正确的处理应该是,对于每个车次中的连续站点,比如tt[1], tt[2], ..., tt[t],那么对于每个i从1到t-1,需要建立tt[i]到tt[i+1]的边,这样就能保证tt[i]的层级比tt[i+1]低。而用户当前的代码则是,对于每个tt[i],所有不在该车次的站点j都向tt[i]建立边,这显然不符合目的要求。这会导致每个车次中的站点被所有不在该车次的站点所指向,这显然不正确。例如,车次中的各个站点之间本应有顺序关系,但用户代码并没有建立这些顺序,反而建立了大量无关的边。 所以,这应该是用户代码的主要错误。比如,在样例输入中,比如当车次是1 3 5 6时,正确的做法是建立1→3,3→5,5→6的边,而用户的代码却将所有不在车次的节点(比如2、4等)向这三个节点建立边,这明显是错误的。这会使得每个车次内的节点之间没有边,反而其他节点指向他们,导致拓扑排序的结果不正确。 所以,用户的问出在处理每个车次的逻辑上。正确的做法是,对于每个车次中的连续站点,建立从前到后的边。而用户的代码错误地将所有不在车次的节点连接到车次中的每个节点,导致车次内部的顺序没有被正确处理,从而层级计算错误。 另外,可能还有其他问。比如,在处理每个车次时,用户使用了两层循环:i从1到t,然后j从1到n,如果j不在当前车次中,就建立j到tt[i]的边。这会导致每个车次中的每个tt[i]都被所有不在车次的节点j所指向。这显然不符合目的要求。比如,假设车次中的节点是按顺序的,那么正确的依赖应该是车次中的前驱节点指向后续节点,而不是其他节点都指向车次中的节点。 举个例子,假设车次是a→b→c,那么正确的边应该是a→b和b→c。而用户的代码则会为每个不在车次中的节点x,建立x→a、x→b、x→c的边,这显然错误。这会导致这些车次内的节点之间没有顺序,反而其他节点都必须在它们前面,这显然不符合意。 所以,用户的主要错误在于边的建立方式。正确的做法应该是在同一个车次中,按顺序建立从前一个节点到后一个节点的边。例如,对于车次的序列tt[1], tt[2], ..., tt[t],应该为每个i从1到t-1,添加边tt[i]→tt[i+1]。这样,在拓扑排序时,就能确保后面的节点的层级比前面的大,从而计算出正确的层级数。 而用户现在的代码中,对于每个tt[i],添加了所有不在车次的节点j到tt[i]的边,这显然不符合目中的条件。所以,这应该是导致答案错误的主要原因。 除此之外,可能还有其他问,比如是否在多次处理车次时重复添加边导致in数组被多次累加,或者是否在拓扑排序的时候层级计算有误。但根据当前的分析,主要的错误应该是在边的建立部分。 比如,当处理每个车次时,正确的做法是处理车次内部节点的顺序,而用户代码处理的是车次节点和其他节点的关系。这显然是错误的。 总结:用户的代码在处理每个车次的输入时,错误地将所有不在该车次的节点连接到车次内的每个节点,而正确的做法应该是在车次内的节点之间建立顺序边。因此,用户需要修改建立边的部分,将原来的循环改为处理车次内部的前后节点关系。</think>在分析你的代码后,发现主要问出在**边的建立逻辑**上。目要求处理车站的层级关系时,应按照每个车次的站点顺序建立边,而你的代码错误地将所有不在车次中的站点连接到车次内的站点。以下是详细解释和修正步骤: --- ### **错误原因分析** 1. **边的建立方向错误** - 目中每个车次的站点序列 $S_1, S_2, \dots, S_t$ 表示 $S_1$ 的层级必须低于 $S_2$,$S_2$ 的层级低于 $S_3$,依此类推。因此需要为每个相邻站点建立边 $S_i \rightarrow S_{i+1}$。 - 你的代码中,对于每个车次的站点 $S_i$,会遍历所有不在车次的节点 $j$,建立边 $j \rightarrow S_i$。这会导致大量无关的边,且未处理车次内部的顺序。 --- ### **修正步骤** 1. **调整边的建立逻辑** 对每个车次的站点序列,遍历相邻的站点对,建立从前到后的边。例如,输入车次为 $3,5,6$,应建立 $3 \rightarrow 5$ 和 $5 \rightarrow 6$ 的边。 2. **更新拓扑排序的层级计算** 确保每个节点的层级是前驱节点的最大值加1,而不是直接累加。 --- ### **修正后的代码** ```cpp #include<bits/stdc++.h> using namespace std; const int line=2e5+10, dot=1100; struct edge { int to, next; } e[line]; int ans, cnt=0, n, m, h[dot], in[dot]; bool vis[dot]; void add(int u, int v) { e[++cnt] = {v, h[u]}; h[u] = cnt; in[v]++; // 入度增加 } void tuopu() { queue<pair<int, int>> q; // 存储节点及其层级 vector<int> level(dot, 1); // 初始层级为1 for (int i=1; i<=n; i++) if (in[i] == 0) q.push({i, 1}); while (!q.empty()) { auto [u, l] = q.front(); q.pop(); ans = max(ans, l); for (int i=h[u]; i; i=e[i].next) { int v = e[i].to; if (--in[v] == 0) { level[v] = max(level[v], l+1); q.push({v, level[v]}); } } } } int main() { cin >> n >> m; for (int i=1; i<=m; i++) { int t; cin >> t; vector<int> tt(t); for (int j=0; j<t; j++) cin >> tt[j]; // 建立车次内部的边:tt[j] -> tt[j+1] for (int j=0; j<t-1; j++) { add(tt[j], tt[j+1]); } } tuopu(); cout << ans; return 0; } ``` --- ### **关键改动说明** 1. **边的建立方式** 将原来的双重循环(遍历所有不在车次的节点)改为遍历车次内部相邻的站点,建立从前到后的边。 2. **层级计算优化** 使用 `level` 数组记录每个节点的最大层级,确保正确传递前驱节点的层级。 --- ### **测试用例验证** 假设输入: ``` 6 2 3 1 3 5 3 3 5 4 ``` - 第一个车次:1→3→5 - 第二个车次:3→5→4 建立的边应为:1→3, 3→5, 5→4, 3→5(重复边不影响拓扑排序)。最终层级为1(1)→2(3)→3(5)→4(4),输出4。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值