Newcoder 142 A.Ternary String(指数循环定理)

本文探讨了在特定字符串操作下,如何通过模运算优化算法,实现高效计算字符串变为空串所需的步骤。针对由0,1,2组成的字符串,文章详细解析了递归算法与指数循环定理的应用,提供了时间复杂度为O(n)的解决方案。

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

Description

给出一个只有0,1,20,1,2组成字符串,每一步操作,首先每个11后面多出一个0,每个22后面多出一个1,然后第一个数字消失,问多少次操作后该字符串变成空串,如果该字符串不可能变成空串则输出1−1

Input

第一行输入一整数TT表示用例组数,每组用例输入一个只由0,1,2组成的字符串ss

(1|s|105,|s|2106)

Output

对于每组用例,输出消除字符串ss所需要的操作数,结果模109+7,如果该字符串不可能变成空串则输出1−1

Sample Input

3
000
012
22

Sample Output

3
93
45

Solution

对于aiai,假设消除a1,...,ai1a1,...,ai−1需要xx次操作,记消除ai及其延伸出的数字需要的操作次数为Solve(i,x)Solve(i,x),简单递推有

Solve(i,x)=1x+232x+1x3ai=0ai=1ai=2Solve(i,x)={1ai=0x+2ai=13⋅2x+1−x−3ai=2

f(i)f(i)表示消除a1,...,aia1,...,ai所需的操作数,那么有以下转移
f(i)=f(i1)+12(f(i1)+1)3(2f(i1)+11)ai=0ai=1ai=2f(i)={f(i−1)+1ai=02⋅(f(i−1)+1)ai=13⋅(2f(i−1)+1−1)ai=2

因为每次遇到22都会将之前的答案放在指数上,由指数循环定理AB%C=AB%φ(C)+φ(C)%C,Bφ(C),只要根据当前位后面有几个22来修改模数即可,例如后面有一个2的时候需要模φ(109+7)=109+6φ(109+7)=109+6,后面有两个22的时候需要模φ(109+6),以此类推,注意要开一个标记变量记录之前的答案与当前模数欧拉函数值的大小关系来判断是否需要在幂指数上多加一个欧拉函数值,时间复杂度O(n)O(n)

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
#define maxn 100005 
int T,mod[30]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,5242880,19660800,79872000,243900800,500000002,1000000006,1000000007};
char s[maxn];
int mul(int x,int y,int c)
{
    ll z=1ll*x*y;
    return z-z/c*c;
}
int add(int x,int y,int c)
{
    x+=y;
    if(x>=c)x-=c;
    return x;
}
int Pow(int a,int b,int c)
{
    int ans=1;
    while(b)
    {
        if(b&1)ans=mul(ans,a,c);
        a=mul(a,a,c);
        b>>=1;
    }
    return ans;
}
int euler(int n)
{
    int ans=n;
    for(int i=2;i*i<=n;i++)
        if(n%i==0)
        {
            ans=ans/i*(i-1);
            while(n%i==0)n/=i;
        }
    if(n>1)ans=ans/n*(n-1);
    return ans;
}
P Solve(int n,int i)
{
    if(n==0)return P(0,0);
    if(i==0)return P(0,1);
    P a;
    if(s[n]=='2')a=Solve(n-1,i-1);
    else a=Solve(n-1,i);
    if(s[n]=='0')
    {
        a.first++;
        return P(a.first%mod[i],a.second||(a.first>=mod[i]?1:0));
    }
    if(s[n]=='1')
    {
        a.first=2*a.first+2;
        return P(a.first%mod[i],a.second||(a.first>=mod[i]?1:0));
    }
    if(s[n]=='2')
    {
        if(a.second)a.first+=mod[i-1];
        if(a.first>=30)
        {
            a.first=mul(3,add(Pow(2,a.first+1,mod[i]),mod[i]-1,mod[i]),mod[i]);
            a.second=1;
            return a;
        }
        else
        {
            ll temp=3ll*((1ll<<(a.first+1))-1);
            return P(temp%mod[i],temp>=mod[i]?1:0);
        }
    }
    return P(0,0);
}
int main()
{
    int res=29;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",s+1);
        int n=strlen(s+1);
        printf("%d\n",Solve(n,res-1).first);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值