数论 + 哈希 - The Oculus - HDU 6768

数论 + 哈希 - The Oculus - HDU 6768

2020 Multi-University Training Contest 2

题意:

定 义 斐 波 那 契 数 列 : F 1 , F 2 , . . . , F 1 = 1 , F 2 = 2 , F i = F i − 1 + F i − 2 ( i ≥ 3 ) 定义斐波那契数列:F_1,F_2,...,F_1=1,F_2=2,F_i=F_{i-1}+F_{i-2}(i≥3) F1,F2,...F1=1,F2=2,Fi=Fi1+Fi2(i3)

众 所 周 知 , 任 意 的 正 整 数 x 可 由 斐 波 那 契 数 列 的 几 项 和 来 表 示 。 众所周知,任意的正整数x可由斐波那契数列的几项和来表示。 x

抽 象 地 , 即 对 任 意 的 正 整 数 x , 存 在 n 元 组 ( b 1 , b 2 , . . . , b n ) , 满 足 : 抽象地,即对任意的正整数x,存在n元组(b_1,b_2,...,b_n),满足: xn(b1,b2,...,bn)

  • b 1 × F 1 + b 2 × F 2 + . . . + b n × F n = x b_1×F_1+b_2×F_2+...+b_n×F_n=x b1×F1+b2×F2+...+bn×Fn=x
  • b n = 1 , 且 对 任 意 i ∈ [ 1 , n − 1 ] , b i ∈ b_n=1,且对任意i∈[1,n-1],b_i∈ bn=1i[1,n1]bi{ 0 , 1 0,1 0,1 } 恒 成 立 。 恒成立。
  • 对 任 意 的 i ∈ [ 1 , n − 1 ] , b i × b i + 1 = 0   恒 成 立 。 对任意的i∈[1,n-1],b_i×b_{i+1}=0\ 恒成立。 i[1,n1]bi×bi+1=0 

例 如 : 4 = ( 1 , 0 , 1 ) , 5 = ( 0 , 0 , 0 , 1 ) , 20 = ( 0 , 1 , 0 , 1 , 0 , 1 ) , 因 为   20 = F 2 + F 4 + F 6 = 2 + 5 + 13. 例如: 4=(1,0,1), 5=(0,0,0,1), 20=(0,1,0,1,0,1) ,因为\ 20=F_2+F_4+F_6=2+5+13. 4=(1,0,1),5=(0,0,0,1),20=(0,1,0,1,0,1) 20=F2+F4+F6=2+5+13.

T 组 测 试 样 例 。 T组测试样例。 T

每 组 测 试 样 例 输 入 三 行 数 据 , 每组测试样例输入三行数据,

每 行 包 括 一 个 长 度 n i , 和 一 个 长 度 为 n i 的 由 0 和 1 构 成 的 序 列 , 每行包括一个长度n_i,和一个长度为n_i的由0和1构成的序列, nini01

分 别 表 示 A 、 B 和 C 的 斐 波 那 契 序 列 的 表 示 。 分别表示A、B和C的斐波那契序列的表示。 ABC

已 知 C 的 斐 波 那 契 表 示 的 某 一 位 的 1 被 修 改 成 了 0 , 请 找 出 这 个 位 置 , 使 得 修 改 后 , 满 足 A × B = C 。 已知C的斐波那契表示的某一位的1被修改成了0,请找出这个位置,使得修改后,满足A×B=C。 C10使A×B=C

Sample Input

1
3 1 0 1
4 0 0 0 1
6 0 1 0 0 0 1

Sample Output

4

数据范围:

1 ≤ T ≤ 10000 ,   1 ≤ ∣ A ∣ , ∣ B ∣ ≤ 1000000 ,   2 ≤ ∣ C ∣ ≤ ∣ A ∣ + ∣ B ∣ + 1 ,   ∑ ∣ A ∣ , ∑ ∣ B ∣ ≤ 5000000 1≤T≤10000,\\ \ \\ 1≤|A|,|B|≤1000000,\\ \ \\2≤|C|≤|A|+|B|+1,\\ \ \\∑|A|,∑|B|≤5000000 1T10000, 1A,B1000000 2CA+B+1 A,B5000000


分析:

本 题 很 自 然 地 能 够 想 到 , 先 求 出 A 和 B , 记 X = A × B − C , 判 断 X 是 斐 波 那 契 数 列 的 第 几 项 。 本题很自然地能够想到,先求出A和B,记X=A×B-C,判断X是斐波那契数列的第几项。 ABX=A×BCX

计 算 A 和 B , 以 及 查 询 X 的 位 置 , 都 需 要 预 处 理 出 斐 波 那 契 数 列 的 部 分 项 。 计算A和B,以及查询X的位置,都需要预处理出斐波那契数列的部分项。 ABX

考 虑 到 C 的 长 度 可 能 达 到 2000001 项 , 故 我 们 要 先 做 大 约 2 × 1 0 6 的 预 处 理 。 考虑到C的长度可能达到2000001项,故我们要先做大约2×10^6的预处理。 C20000012×106

由 于 项 数 较 多 , 已 经 早 早 的 溢 出 了 1 0 18 , 因 此 我 们 计 算 时 , 需 要 对 斐 波 那 契 数 列 取 模 。 由于项数较多,已经早早的溢出了10^{18},因此我们计算时,需要对斐波那契数列取模。 1018

根 据 题 意 , 有 : ( A × B ) % P = ( C + X ) % P 根据题意,有:(A×B)\%P=(C+X)\%P (A×B)%P=(C+X)%P

即 : ( A × B ) % P − C % P = X % P 即:(A×B)\%P-C\%P=X\%P (A×B)%PC%P=X%P

我 们 计 算 出 X % P 后 , 在 预 处 理 的 斐 波 那 契 数 列 中 查 找 X % 所 在 的 位 置 即 可 。 我们计算出X\%P后,在预处理的斐波那契数列中查找X\%所在的位置即可。 X%PX%

( 题 解 直 接 建 议 用 无 符 号 的 长 整 型 , 让 其 自 然 溢 出 . . . ) (题解直接建议用无符号的长整型,让其自然溢出...) (...)

经 过 测 试 , 取 双 模 数 是 可 以 避 免 冲 突 的 。 ( 单 模 数 还 是 冲 突 一 大 堆 ) 经过测试,取双模数是可以避免冲突的。(单模数还是冲突一大堆) ()

因 此 我 们 用 p a i r 来 存 储 对 两 个 不 同 模 数 取 模 后 的 斐 波 那 契 数 列 。 因此我们用pair来存储对两个不同模数取模后的斐波那契数列。 pair

对 A 、 B 、 C 对 两 个 模 数 P 1 和 P 2 取 模 , 接 着 在 预 处 理 的 斐 波 那 契 数 列 中 查 找 ( X 1 , X 2 ) 所 在 的 位 置 即 可 。 对A、B、C对两个模数P_1和P_2取模,接着在预处理的斐波那契数列中查找(X_1,X_2)所在的位置即可。 ABCP1P2(X1,X2)

注意:

最 后 的 查 找 位 置 不 能 用 二 分 , 因 为 取 模 后 , 新 的 序 列 不 是 单 调 序 列 ! 最后的查找位置不能用二分,因为取模后,新的序列不是单调序列!

直 接 遍 历 查 找 即 可 。 直接遍历查找即可。


双模数:
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>

#define ll long long
#define P pair<int,int>
#define x first
#define y second

using namespace std;

const int N=2e6, mod1=805306457, mod2=201326611;

int A[N+10],B[N+10],C[N+10];
P f[N+10];

void Init()
{
    f[1].x=f[1].y=1,f[2].x=f[2].y=2;
    for(int i=3;i<=N+5;i++)
    {
        f[i].x=(f[i-1].x+f[i-2].x)%mod1;
        f[i].y=(f[i-1].y+f[i-2].y)%mod2;
    }
}

int add_mod(int a,int b,int mod)
{
    ll t=0;
    t=a+b;
    if(t>=mod) t%=mod;
    return t;
}

int main()
{
    Init();

    int T,n1,n2,n3;
    cin>>T;
    while(T--)
    {
        P a,b,c;
        int a1=0,a2=0,b1=0,b2=0,c1=0,c2=0;

        scanf("%d",&n1);
        for(int i=1;i<=n1;i++)
        {
            scanf("%d",&A[i]);
            if(A[i]) 
            {
                a1=add_mod(a1,f[i].x,mod1);
                a2=add_mod(a2,f[i].y,mod2);
            }
        }
        a={a1,a2};

        scanf("%d",&n2);
        for(int i=1;i<=n2;i++)
        {
            scanf("%d",&B[i]);
            if(B[i]) 
            {
                b1=add_mod(b1,f[i].x,mod1);
                b2=add_mod(b2,f[i].y,mod2);
            }
        }
        b={b1,b2};
        
        scanf("%d",&n3);
        for(int i=1;i<=n3;i++)
        {
            scanf("%d",&C[i]);
            if(C[i]) 
            {
                c1=add_mod(c1,f[i].x,mod1);
                c2=add_mod(c2,f[i].y,mod2);
            }
        }
        c={c1,c2};
        
        int d1=(ll)a.x*b.x%mod1;
        int d2=(ll)a.y*b.y%mod2;
        for(int i=1;;i++)
            if(f[i].x==(d1-c.x+mod1)%mod1 && f[i].y==(d2-c.y+mod2)%mod2)
            {
                printf("%d\n",i);
                break;
            }
    }

    return 0;
}

自然溢出:
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>

#define ll unsigned long long

using namespace std;

const int N=2e6;

int A[N+10],B[N+10],C[N+10];
ll f[N+10];

void Init()
{
    f[1]=1,f[2]=2;
    for(int i=3;i<=N+5;i++) f[i]=f[i-1]+f[i-2];
}

int main()
{
    Init();

    int T,n1,n2,n3;
    cin>>T;
    while(T--)
    {
        ll a=0,b=0,c=0;

        scanf("%d",&n1);
        for(int i=1;i<=n1;i++)
        {
            scanf("%d",&A[i]);
            if(A[i]) a+=f[i];
        }

        scanf("%d",&n2);
        for(int i=1;i<=n2;i++)
        {
            scanf("%d",&B[i]);
            if(B[i]) b+=f[i];
        }

        scanf("%d",&n3);
        for(int i=1;i<=n3;i++)
        {
            scanf("%d",&C[i]);
            if(C[i]) c+=f[i];
        }

        a*=b;
        for(int i=1;;i++)
            if(c+f[i]==a)
            {
                printf("%d\n",i);
                break;
            }
    }

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值