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

186 篇文章 0 订阅

Description

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

Input

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

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

Output

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

Sample Input

3
000
012
22

Sample Output

3
93
45

Solution

对于 ai a i ,假设消除 a1,...,ai1 a 1 , . . . , a i − 1 需要 x x 次操作,记消除ai及其延伸出的数字需要的操作次数为 Solve(i,x) S o l v e ( i , x ) ,简单递推有

Solve(i,x)=1x+232x+1x3ai=0ai=1ai=2 S o l v e ( i , x ) = { 1 a i = 0 x + 2 a i = 1 3 ⋅ 2 x + 1 − x − 3 a i = 2

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

因为每次遇到 2 2 都会将之前的答案放在指数上,由指数循环定理AB%C=AB%φ(C)+φ(C)%C,Bφ(C),只要根据当前位后面有几个 2 2 来修改模数即可,例如后面有一个2的时候需要模 φ(109+7)=109+6 φ ( 10 9 + 7 ) = 10 9 + 6 ,后面有两个 2 2 的时候需要模φ(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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值