P1037 [NOIP2002 普及组] 产生数


原题链接

P1037
题目类型: 普 及 + / 提 高 {\color{green}{普及+/提高}} +/
AC记录:Accepted

题目大意

给定一个数字 n n n k k k个规则,规则的内容是可以让一个一位数变成另一个一位数,求在这些规则下,可能产生多少个数(包括自己)。

输入格式

第一行两个整数 n , k n,k n,k
接下来 k k k行,每行两个整数 x i , y i x_i,y_i xi,yi,代表可以从 x i x_i xi转换成 y i y_i yi

输出格式

输出能生成的数字个数。
S a m p l e \mathbf{Sample} Sample I n p u t \mathbf{Input} Input

234 2
2 5
3 6

S a m p l e \mathbf{Sample} Sample O u t p u t \mathbf{Output} Output

4

H i n t & E x p l a i n \mathbf{Hint\&Explain} Hint&Explain
在样例中,有以下 2 2 2个规则: 2 → 5 , 3 → 6 2\to 5,3\to 6 25,36
234 234 234可以变成 234 , 534 , 264 , 564 234,534,264,564 234,534,264,564,总共 4 4 4种,答案为 4 4 4

数据范围

对于 100 % 100\% 100%的数据, 1 ≤ n ≤ 1 0 30 , 1 ≤ k ≤ 15 1≤n≤10^{30},1≤k≤15 1n1030,1k15

解题思路

我第一次看到这道题时,就想着可以把可能变化到的数字个数乘起来,得到答案。
实际上,这种思路并没有错,但是难点在于:如何找出一个数字可能变化到的数字?
这里就要涉及到一个判断图中的两点是否连通的思路,这里用的是类似 F l o y e d Floyed Floyed的算法。
你可以想,如果你像 F l o y e d Floyed Floyed一样,枚举一个中间点,判断他可不可通的话,这里就要用一个式子:设 f α , β f_{\alpha,\beta} fα,β 0 0 0 α , β \alpha,\beta α,β两点没有连边,为 1 1 1 α , β \alpha,\beta α,β两点有连边, k k k为中间点。则f[i][j] = f[i][j] || ( f[i][k] && f[k][j] )f[i][k] && f[k][j]是判断 i → k i\to k ik k → j k\to j kj是否都相通,而前面用一个||都很清楚,只要可行,就可以设为true
这样一来,数字是否相通的问题就解决了。
但是,有个问题,数字乘起来爆unsigned long long怎么办?开高精度。
或者根据我们机房的某位大佬 U n n a m e d _ C u b e \mathrm{\color{grey}U\color{red}{nnamed\_Cube}} Unnamed_Cube所说,本题可以用__int128_t水过。


最后,祝大家早日
AC

上代码

#include<bits/stdc++.h>

using namespace std;

string  s;
bool    road[20][20];
int     can_be[20];
int     mul[10010];
int     n;

int main()
{
    cin>>s>>n;
    for(int i=0; i<=9; i++)
        road[i][i]=true;
    for(int i=1; i<=n; i++)
    {
        int x,y;
        cin>>x>>y;
        road[x][y]=true;
    }
    for(int k=0; k<=9; k++)
        for(int i=0; i<=9; i++)
            for(int j=0; j<=9; j++)
                road[i][j]=road[i][j]||(road[i][k]&&road[k][j]);
    for(int i=0; i<=9; i++)
        for(int j=0; j<=9; j++)
            if(road[i][j])
                can_be[i]++;
    mul[10000]=1;
    int up=0;
    for(int i=0; i<s.size(); i++)
    {
        int num=can_be[s[i]-'0'];
        for(int j=10000; j>=1; j--)
        {
            mul[j]=mul[j]*num+up;
            up=mul[j]/10;
            mul[j]%=10;
        }
    }
    int pos=1;
    while(pos<10000&&mul[pos]==0)
        pos++;
    for(; pos<=10000; pos++)
        cout<<mul[pos];
    cout<<endl;
    return 0;
}

完美切题 ∼ \sim

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值