牛客网暑期ACM多校训练营(第四场)A 题解 (欧拉降幂)
题意:给你一个长度小于1e5的串,每次进行一次操作,在字符1后面插入一个0.在字符2后面插入一个1,然后删除首字符。
然后问你需要操作多少次,字符串会变成空串。
先写个暴力程序找找规律。
下面给出我的暴力代码。
#include
using namespace std;
string ch(string s)
{
int n=s.size();
string ans;
for(int i=0;i<n;i++)
{
if(i!=0) ans+=s[i];
if(s[i]=='1')
{
ans+='0';
}
else if(s[i]=='2')
{
ans+='1';
}
}
return ans;
}
int main()
{
string s;
while(cin>>s)
{
int ans=0;
while(1)
{
if(s.size()==0) break;
//cout<<s<<endl;
s=ch(s);
ans++;
}
cout<<ans<<endl;
}
return 0;
}
每次我们加个1,假设没加1之前的操作次数是x,那么加了1之后操作次数就是2x+2。
每次我们加个2,假设没加2之前的操作次数是x,那么加了2之后的操作次数就变成3*(2^(x+1)-1),这个怎么找到的呢。
我们看到类似9,21,45,93这种都是3的倍数,我们把他们都除以3,得到3,7,15,31...发现他们都是2^n-1,那么规律就找到了。
当我们每次加个0,操作次数就加1,这个没啥说的。
到这时我们递推一下就行了。
1: x+x+2
2: 3*2^(x+1)
0: x+1
然后我们交上去发现超时了%50!!难道评测姬坏掉了QAQ
没超时的大佬下面的不用看了~~~
仔细观察发现如果每次碰到2直接用快速幂算2^(x+1),那么n有2e6。极端情况下碰到全为2的串。我们每次都要算一次快速幂。那么评测姬
就会类似于这样,咳咳!!然而我们必须每次都算一次快速幂。
那么如何优化类似于(a^b^c^d^e.....)%mod这种式子。需要用到一个叫欧拉降幂的知识 (先%一下欧拉orz)。
关于欧拉降幂我也是刚学的,然后学的东西写这篇博客里了
https://blog.csdn.net/qq_37632935/article/details/81264965
那么学会欧拉降幂后这题就可以预处理出(ph...(ph(ph(1e9+7)))的所有欧拉函数值了。不能每次都求,要把它们先记下来,不然还是会T。
给出代码
#include
using namespace std;
typedef long long ll;
map m;
ll PH(ll x)
{
ll res=x,a=x;
for(ll i=2;i*i<=x;i++)
{
if(a%i==0)
{
res=res/i*(i-1);
while(a%i==0) a/=i;
}
}
if(a>1) res=res/a*(a-1);
return res;
}
ll quick_pow(ll a,ll b,ll mod)
{
ll ans=1;
while(b)
{
if(b&1) ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
char s[100005];
ll dfs(ll i,ll mod)
{
if(i==0) return 0;
else if(s[i]=='0') return (dfs(i-1,mod)+1)%mod;
else if(s[i]=='2') return ((3ll*quick_pow(2,dfs(i-1,m[mod])+1,mod)-3)%mod+mod)%mod;
else return (2*dfs(i-1,mod)+2)%mod;
}
int main()
{
ll mo=1e9+7;
while(mo!=1)
{
m[mo]=PH(mo);
mo=m[mo];
}
m[1]=1;
int T;
scanf("%d",&T);
while(T--)
{
ll mod=1e9+7;
cin>>s+1;
int n=strlen(s+1);
printf("%lld\n",dfs(n,mod));
}
return 0;
}
/*
1 x+2
2 3*2^(x+1)-x
*/ 贴一个循环的代码。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mod[100005];
ll quick_pow(ll a, ll b, ll mo)
{
ll res = 1;
while(b)
{
if(b & 1)
res = res * a % mo;
b >>= 1;
a = a * a % mo;
}
return res;
}
ll euler(ll n) //??euler(n)
{
ll res = n, a = n;
for(ll i = 2; i * i <= a; i++)
{
if(a % i == 0)
{
res = res / i * (i - 1); //?????????????????
while(a % i == 0) a /= i;
}
}
if(a > 1) res = res / a * (a - 1);
return res;
}
char s[100005];
int main()
{
mod[0] = 1e9 + 7;
for(int i = 1; i < 1e5 + 5; i++)
{
mod[i] = euler(mod[i - 1]);
// printf("%d---%lld\n",i,mod[i]);
}
int t;
scanf("%d", &t);
while(t--)
{
scanf("%s", s);
int cnt = 0;
for(int i = 0; s[i] != 0; i++)
{
if(s[i] == '2')
cnt++;
}
ll ans = 0;
for(int i = 0; s[i] != 0; i++)
{
if(s[i] == '0')
{
ans++;
ans %= mod[cnt];
}
else if(s[i] == '1')
{
ans = (ans + ans + 2) % mod[cnt];
}
else
{
cnt--;
if(ans<mod[cnt])
ans = 3ll * (quick_pow(2ll, (ans + 1) % mod[cnt], mod[cnt]) - 1 + mod[cnt]) % mod[cnt];
else
{
ans = 3ll * (quick_pow(2ll, (ans + 1) % mod[cnt]+mod[cnt], mod[cnt]) - 1 + mod[cnt]) % mod[cnt];
}
}
}
printf("%lld\n", ans);
}
}
查看30道真题和解析