NowCoder2018多校A Ternary String 拓展欧拉定理
题意
给定带前导0的三进制数,每秒自动进行一次操作,1的后面多一个0,2的后面多一个1,开头的字符被删除,求这个数变为空的操作次数
求操作次数取模\(1e9+7\)
\[1 \leq |s| \leq 10^5 \]分析
考虑记录时间,原串中每个数带来的贡献,可以简单归纳得出
0:\(t + 1\)
1:\(2(t + 1)\)
2:\(3(2^{t + 1} - 1)\)
操作12都可以轻松得到结果,对于3,计算\(2^{t + 1} \ mod \ MOD\)是不方便的,考虑使用拓展欧拉定理
\[a^b \equiv \begin{cases} a^{b \bmod \varphi(m)},gcd(a,m) = 1\\ a^b ,gcd(a,m) \neq 1,b \leq \varphi(m) (mod \ m)\\ a^{b \ mod \varphi(m) +\varphi(m)},gcd(a,m) \neq 1,b \geq \varphi(m) \end{cases} \]使用时,要求幂对\(\varphi(m)\)取模,因此我们记录\(t\)时还需额外记录\(\varphi(m)\)意义下的\(t\),维护\(\varphi(m)\) 意义下的\(t\)又需要嵌套上模\(\varphi(\varphi(m))\)
为了方便实现,可以采用DFS的方式记录当前第几个数和当前的模数,至于为什么不用判断\(b \geq \varphi(m)\),我也不是很懂
代码
const int MOD = (int)1e9 + 7;
int m[35],ans[35];
inline int mul(int a,int b,int i){
return (ll)a * b % i;
}
inline void add(int &a,int b,int i){
a += b;
if(a >= i) a -= i;
}
inline int ksm(int a,ll b = MOD - 2,int m = MOD){
int ans = 1;
int base = a;
while(b){
if(b & 1) {
ans = (ll)ans * base % m;
}
base = (ll)base * base % m;
b >>= 1;
}
return ans;
}
inline int get_phi(int n) {
int m = int(sqrt(n + 0.5));
int ans = n;
for (int i = 2; i <= m; 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;
}
unordered_map<int,int> mp;
char s[100005];
int dfs(int len,int m){
if(!len) return 0;
if(s[len] == '0')
return (dfs(len - 1,m) + 1) % m;
if(s[len] == '1')
return (ll)2 * (dfs(len - 1,m) + 1) % m;
//if(mp[m] != 1) return (mul(3,ksm(2,(dfs(len - 1,mp[m]) + 1) % mp[m],m),m) + m - 3) % m;
int p = dfs(len - 1,mp[m]) + 1;
if(m != 1 && m != MOD) {
if(p >= mp[m]) return (mul(3,ksm(2,p % mp[m] + mp[m],m),m) + m - 3) % m;
else return (mul(3,ksm(2,p % mp[m],m),m) + m - 3) % m;
}
return (mul(3,ksm(2,p % mp[m],m),m) + m - 3) % m;
}
int main(){
int T = rd();
int tmp = MOD;
mp[1] = 1;
while(tmp > 1) {
mp[tmp] = get_phi(tmp);
tmp = mp[tmp];
}
while(T--){
scanf("%s",s + 1);
int len = strlen(s + 1);
printf("%d\n",dfs(len,MOD));
}
}