【五校联考1day2】JZOJ2020年8月12日提高组T1 对你的爱深不见底

【五校联考1day2】JZOJ2020年8月12日提高组T1 对你的爱深不见底

题目

Description

出乎意料的是,幸运E 的小R 居然赢了那个游戏。现在欣喜万分的小R 想要写一张明信片给小Y,但是因为小R 非常羞涩,所以他打算采用一些比较神奇的方式来表达。
他定义了一些字符串,s1 = a,s2 = b,si =s_i-1 + s_i-2 (i >=3)。同时他定义了一个字符串s 的权值为一个最大的i <|s|满足s 长度为i 的前缀等于长度为i 的后缀。比如字符串aba 的权值就是1,abab 的权值就是2,aaaa 的权值就是3。
现在小R 在明信片上给出了两个数n 和m,他想要告诉小Y 的信息是字符串sn 的前m个字符组成的字符串的权值。你可以帮小Y 计算一下吗?

Input

第一行输入一个正整数T 表示数据组数。
对于每组数据,第一行是两个整数n;m。保证1<= m <=|sn|

Output

对于每组数据,输出一个整数表示答案。答案可能很大,你只需要输出模258280327 后的答案。

Sample Input

2
4 3
5 5

Sample Output

1
2

Data Constraint

对于30% 的数据,n <= 20
对于60% 的数据,n <= 60
对于100% 的数据,n <= 10^3,1 <= T <= 100

题解

题意

定义一些字符串, s 1 = a s_1=a s1=a s 2 = b s_2=b s2=b s i = s i − 1 + s i − 2 s_i=s_{i-1}+s_{i-2} si=si1+si2(例如 s 3 s_3 s3 b a ba ba
s n s_n sn的前 m m m个字符的权值
权值定义:对于一个字符串,前缀和后缀的相同部分的长度

分析

通过打表发现规律
找到一个最小 n n n满足
∣ s [ n ] ∣ > m + 1 |s[n]|>m+1 s[n]>m+1
那么答案就是 m − ∣ s [ n − 2 ] ∣ m-|s[n-2]| ms[n2]
那么直接求解即可,用高精度

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 258280327
using namespace std;
struct gjd
{
    int a[505],len;
}f[10005],Ans;
int n,k,i,t,l,r,mid,ans,dr[10005];
char ch;
bool judge(gjd &x,gjd &y)
{
    int i;
    if (x.len<y.len) return true;
    if (x.len>y.len) return false;
    for (i=x.len;i;i--)
    {
        if (x.a[i]<y.a[i]) return true;
        if (x.a[i]>y.a[i]) return false;
    }
    return false;
}
void fibonacci(gjd &a,gjd &b,gjd &c)
{
    int i;
    for (i=1;i<=c.len;i++)
        c.a[i]=0;
    c.len=0;
    for (i=1;i<=a.len||i<=b.len;i++)
    {
        c.a[i]+=a.a[i]+b.a[i];
        c.a[i+1]+=c.a[i]/10;
        c.a[i]%=10;
    }
    c.len=max(a.len,b.len);
    while (c.a[c.len+1])
    {
        c.len++;
        c.a[c.len+1]=c.a[c.len]/10;
        c.a[c.len]%=10;
    }
}
int main()
{
    freopen("test.in","r",stdin);
    freopen("test.out","w",stdout);
    f[1].a[1]=1;
    f[1].len=1;
    f[2]=f[1];
    for (i=3;i<=1001;i++)
        fibonacci(f[i-2],f[i-1],f[i]);
    scanf("%d",&t);
    while (t--)
    {
        scanf("%d",&n);
        Ans.len=0;
        memset(Ans.a,0,sizeof(Ans.a));
        ch=getchar();
        while (ch<'0'||ch>'9') ch=getchar();
        while (ch>='0'&&ch<='9')
        {
            Ans.len++;
            dr[Ans.len]=ch-'0';
            ch=getchar();
        }
        for (i=1;i<=Ans.len;i++)
            Ans.a[Ans.len-i+1]=dr[i];
        Ans.a[1]++;
        for (i=1;i<=Ans.len;i++)
        {
            Ans.a[i+1]+=Ans.a[i]/10;
            Ans.a[i]%=10;
        }
        if (Ans.a[Ans.len+1]) Ans.len++;
        l=1;
        r=1001;
        k=0;
        while (l<=r)
        {
            mid=(l+r)>>1;
            if (judge(Ans,f[mid]))
            {
                k=mid;
                r=mid-1;
            }
            else l=mid+1;
        }
        k-=2;
        Ans.a[1]--;
        for (i=1;i<=f[k].len;i++)
        {
            Ans.a[i]-=f[k].a[i];
            if (Ans.a[i]<0)
            {
                Ans.a[i]+=10;
                Ans.a[i+1]--;
            }
        }
        while (!Ans.a[Ans.len]&&Ans.len>1) Ans.len--;
        ans=0;
        for (i=Ans.len;i;i--)
            ans=((long long)ans*10+Ans.a[i])%mod;
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值