牛客网暑期ACM多校训练营(第四场) A.Ternary String (找规律+欧拉降幂)

题目链接

时间限制:C/C++ 4秒,其他语言8秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld

题目描述

A ternary string is a sequence of digits, where each digit is either 0, 1, or 2.
Chiaki has a ternary string s which can self-reproduce. Every second, a digit 0 is inserted after every 1 in the string, and then a digit 1 is inserted after every 2 in the string, and finally the first character will disappear.
For example, ``212'' will become ``11021'' after one second, and become ``01002110'' after another second.
Chiaki would like to know the number of seconds needed until the string become an empty string. As the answer could be very large, she only needs the answer modulo (109 + 7).

输入描述:

There are multiple test cases. The first line of input is an integer T indicates the number of test cases. For each test case:
The first line contains a ternary string s (1 ≤ |s| ≤ 105).
It is guaranteed that the sum of all |s| does not exceed 2 x 106.

输出描述:

For each test case, output an integer denoting the answer. If the string never becomes empty, output -1 instead.

 

示例1

输入

3
000
012
22

输出

3
93
45

题意:一共有T组样例,每组样例给出一个字符串,串内只含有0,1,2三种数字.时间从0开始,每过一秒,字符串中所有1的后面会插入一个0,每个2后面会插入一个1,然后字符串的第一个数字被删除,询问多少秒后字符串为空串?(结果对1e9+7取模,若是不可能输出-1)

题解:由于0后面不会再插入数字,很显然再有限的时间内一定能够跑出结果,所以-1的情况是不可能出现的.暴力的做法明显是无法实现的,那么我们需要考虑的是对于每个数字作为字符串首时候被删去前的时间 t 删去该数字及其在 t 时间内其后插入的所有数字删去一共花费的时间 t1 ,那么对于下一个数字而言,删去它之前已经经历了 t + t1 的时间,最终遍历完整个字符串即可得到ans.

        那么我们就需要计算的是数字0,1,2所对应的等待时间 t 后被删去后的时间 t' 分别所对应的公式~

        对于0而言,很显然无论等待时间为多少都不会在其后插入数字了,所以 t' = t +1 ;

        对于1而言,我们可以通过暴力打表如下图,可以知道 t' = 2* t + 2 ;

        对于2而言,我们也可以通过暴力打表如下图,可以知道 t' = 3*2^{t+1}-3=6*2^{t}-3 ;

        

        不过由于计算量太大,需要用到取模运算,对于0和1公式都只存在加和乘运算,对结果产生不了影响,可是对于2的时候存在求2^{t+1}的运算,然后以该结果作为后面运算的t来运算,模运算的结果发生了变化,若是不会发生变化,都知道是用快速幂来计算,可是为了消除幂运算后产生的变化,我们需要通过计算mod的欧拉值phi,计算了几次就需要取几次mod的欧拉值,即phi(phi(mod))...

       这些可以通过dfs来实现,从字符串最后一项向前dfs过程中,遇到2的时候将mod修改为当前mod的欧拉值,对于dfs,他会先计算字符串首的时间返回给后面,从而最终是从首项开始计算时间 t .

       还有需要注意的是需要提前预处理出mod的欧拉值phi(mod),和phi(mod)的欧拉值phi(phi(mod))........以便之后使用时接近O(1)的时间内在map中查询得到结果,听说不弄会TLE.

代码如下:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<string>
#include<cstring>
#include<queue>
#include<map>
using namespace std;
#define ll long long
#define inf (ll)0x3f3f3f3f
const int maxn = 1e5 + 10;
const ll mod = 1e9 + 7;
map<ll, ll>phi;                //phi[x]=x的欧拉值
char s[maxn];
ll tmp;                        //用于记录时间的实际值(不过大于1e9+7就都为1e9+7,避免爆炸)
ll euler(ll n) {               //log(n)时间内求一个数的欧拉值
	ll ans = n;
	for (ll i = 2; i*i <= n; i++) {
		if (n%i == 0)
		{
			ans -= ans / i;
			while (n%i == 0) n /= i;
		}
	}
	if (n > 1) ans -= ans / n;
	return ans;
}
void fun() {                   //预处理出mod=1e9+7的所有phi,eg:phi(mod),phi(phi(mod))...
	ll x = mod;
	while (x != 1) {
		phi[x] = euler(x);
		x = phi[x];
	}
	phi[1] = 1;
}
ll mod_pow_max(ll a, ll n) {   //ret<="mod"的快速幂(这里的mod只是利用该题的最大模数)
	ll ret = 1;
	while (n) {
		if (n & 1) ret = min(inf, ret*a);
		a = min(inf, a*a);
		n >>= 1;
	}
	return ret;
}
ll mod_pow(ll a, ll n, ll MOD) {//快速幂取膜
	a %= MOD;
	ll ret = 1;
	while (n) {
		if (n & 1) ret = (ret*a) % MOD;
		a = a*a%MOD;
		n >>= 1;
	}
	return ret;
}
ll dfs(int x, ll MOD) {        //从后往前dfs从而先计算前面的
	if (x == 0) {
		tmp = 0;
		return 0;
	}
	ll t;
	if (s[x] == '0') {
		t = (dfs(x - 1, MOD) + 1) % MOD;
		tmp = min(inf, tmp + 1);
	}
	else if (s[x] == '1') {
		t = ((ll)2 * dfs(x - 1, MOD) + (ll)2) % MOD;
		tmp = min(inf, (ll)2 * tmp + 2);
	}
	else {
		ll exp = dfs(x - 1, phi[MOD]);
        //判断下一层结束的"实际时间"是否大于phi(MOD),从而调用欧拉函数的不同式子
		if (tmp >= phi[MOD])      
			t = ((ll)6 * mod_pow(2, exp + phi[MOD], MOD) - (ll)3 + MOD) % MOD;
		else
			t = ((ll)6 * mod_pow(2, exp, MOD) - (ll)3 + MOD) % MOD;
		tmp = min(inf, (ll)6 * mod_pow_max(2, tmp) - 3);
	}
	return t;
}
int main()
{
	fun();
	int t;
	scanf("%d", &t);
	while (t--) {
		scanf("%s", s + 1);
		int len = strlen(s + 1);
		printf("%lld\n", dfs(len, mod));
	}
	return 0;
}

        做法参考之前写的这题:https://blog.csdn.net/weixin_41156591/article/details/79936030

        下面补充一点知识,也是后来才学习知道,欧拉降幂公式实际上是有2条的,分情况使用:

       \left\{\begin{matrix} A^{B}mod (C)=A^{Bmod(phi(C))}modC& (B<phi(C)) \\ A^{B}mod (C)=A^{Bmod(phi(C))+phi(C)}modC& (B>=phi(C)) \end{matrix}\right.

       以上代码是正式修正后的AC代码了,感谢评论区博友的提点~

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值