XJOI NOIP16提高组赛前训练18-day2 T1:友好数对(数论)

XJOI NOIP16提高组赛前训练18-day2 T1:友好数对

题目描述:

如果一个数a能由一个数b旋转得到,那么我们称< a,b >为友好数对,如12345和45123为友好数对,12345和54321不为友好数对.给出两个正整数L,R,求有多少友好数对< a,b >,满足L<=a

输入格式:

第一行一个整数T,表示数据组数,每组数据两个正整数L,R.

输出格式:

对于每组数据,输出一个整数表示答案.

样例输入:

4
1 9
10 40
100 500
1111 2222

样例输出:

0
3
156
287

数据范围:

对于30%的数据满足L,R<=1000
对于100%的数据满足L,R<=2000000,T<=30,L,R位数相同。

时间限制:

2S

空间限制:

256M

题目分析:

(感觉题目描述有毒,完全没看出来他想说什么样的数对是友好数对)
先解释一下友好数对是什么,先把数理解成一个字符环,那么从某处切开形成的数就是他的友好数,例如1735可以变成7351,3517,5173,那么实际上这几个数都互为友好数对.那么对于一个数来说,他的友好数集合是一定的,而且只要两个数不互为友好数,那么他们的友好数集合一定不相交.由此可以通过一个数,求出他的集合,并记录形成的友好数对,这样对于每一个数都只会判断一次,且答案不会有重复的数对.

代码:

#include
#include
#include
#include

using namespace std;

typedef long long ll;
const int maxn=2000000+10;

int read()
{
    char ch=getchar();int ret=0,flag=1;
    while(ch<'0'||ch>'9') {if(ch=='-') flag=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*flag;
}

int c10[10];

void init()
{
    c10[0]=1;
    for(int i=1;i<=8;i++) c10[i]=c10[i-1]*10;
}

int degit(int n)//计算n的数位
{
    if(n==0) return 1;
    int l=0;
    while(n>0) n/=10,++l;
    return l;
}

ll ans;
int vis[maxn],L,R;

void count(int n)//对于每一个友好数集合的计算
{
    vis[n]=1;
    int l=degit(n),cnt=0;
    for(int i=1;iint t=n/c10[i]+n%c10[i]*c10[l-i];
        if(t>=L&&t<=R&&!vis[t]) vis[t]=1,ans+=++cnt;//判断现在生成的这一个友好数是否被算过,还要考虑t是否在[L,R]的范围内.对于新的一个数可以与前面所有数形成友好数对,对于答案的贡献是cnt
    }
}

ll slove()
{
    ans=0;
    for(int i=L;i<=R;i++) if(!vis[i]) count(i);//如果该数未被标记,则其集合一定未被计算过
    for(int i=L;i<=R;i++) vis[i]=0;//多组数据,标记清除
    return ans;
}

int main()
{
    init();
    int T=read();
    while(T--) {
        L=read(),R=read();
        printf("%lld\n",slove());
    }
    return 0;
}

你可能感兴趣的:(数学-数论,模拟赛)