P1643 完美数
P1643 完美数
思路
一道折磨人的高精度......
可以想象的是,对于一个回文数,我们只需要看前
那么一种暴力的思路就是对着前
这种做法时间复杂度看起来不是很大,只需要加大约
这个时候我们想,既然已经知道了这个数距离
有什么用呢?考虑我们知道
现在的问题就变成了求
看似到这里就做完了,但是依旧存在和第一个做法一样的问题:由于计算的数过大,直接相加或相乘会浪费大量的时间,于是我们可以直接存储计算结果:比如要算
结果就是跑得飞快,平均不超过 10ms。
最后不要忘记判断一下回文串长度的奇偶性就可以了。
时间复杂度约为
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using std::cin;using std::cout;
constexpr int N=20005;
int t;
struct Bigint{//高精度模板部分
int a[N],len=0;
inline Bigint(int x=0){
len=0;memset(a,0,sizeof(a));
for(int i=x;i;i/=10) a[++len]=i%10;
}
inline void scan(){
std::string s;cin>>s;len=s.size();memset(a,0,sizeof(a));
for(int i=0;i<len;++i) a[len-i]=s[i]-'0';
}
inline int& operator[](int i){return a[i];}
inline void flatten(int L){
len=L;
for(int i=1;i<=len;++i){a[i+1]+=a[i]/10;a[i]%=10;}
for(;!a[len]&&len>1;--len);
}
inline void print(std::string s=""){for(int i=len;i;--i) cout<<a[i];cout<<s;}
inline void reflectl(){for(int i=1;i<=len/2;++i) a[i]=a[len-i+1];}//翻转数的前半部分,用于还原前缀
inline Bigint substr(int l,int r){//提取数的部分,用于提取前缀
Bigint c;c.len=r-l+1;
for(int i=1;i<=c.len;++i) c[i]=a[l+i-1];
return c;
}
}x,n,ans,now1,now2,zero(0);
inline bool operator<(Bigint a,Bigint b){
if(a.len!=b.len) return a.len<b.len;
for(int i=a.len;i;--i)
if(a[i]!=b[i])
return a[i]<b[i];
return 0;
}
inline Bigint operator+(Bigint a,Bigint b){
Bigint c;int len=std::max(a.len,b.len)+1;
for(int i=1;i<=len;++i) c[i]=a[i]+b[i];
c.flatten(len);
return c;
}
inline Bigint operator-(Bigint a,Bigint b){
Bigint c;
for(int i=1;i<=a.len;++i){
if(a[i]<b[i]){a[i]+=10;--a[i+1];}
c[i]=a[i]-b[i];
}
c.flatten(a.len);
return c;
}
inline Bigint operator*(Bigint a,int b){
Bigint c;
for(int i=1;i<=a.len;++i) c[i]=a[i]*b;
c.flatten(a.len+11);
return c;
}
inline Bigint operator<<(Bigint a,int b){
Bigint c;
for(int i=1;i<=a.len;++i) c[i+b]=a[i];
c.flatten(a.len+b);
return c;
}//左移数,用于还原前缀
inline Bigint rank(Bigint k){//计算前面有多少回文数
Bigint ans(0),tot;
int q=(k.len-1)/2;//直接计算答案
if(q==0){ans.len=1;ans[1]=0;}
else{
ans.len=q+1;
ans[1]=8;ans[q+1]=1;
for(int i=2;i<=q;++i) ans[i]=9;
}
if(k.len%2==0){
if(q){ans[ans.len]=0;ans[++ans.len]=1;}
else ans[1]=9;
}
tot.len=q+1;tot[q+1]=1;
ans=ans+k.substr(k.len-(k.len+1)/2+1,k.len);
ans=ans-tot+Bigint(1);//ans 计算应有回文个数
Bigint w=k;w.reflectl();
if(k<w) ans=ans-Bigint(1);//判断 w 是否可行
return ans;
}
inline Bigint get(Bigint x){//计算排名对应的回文数
Bigint ans(0),tot(0),nxt(0);int b=1;//初始长度为 1,是奇前缀
int q=x.len,cnt=0;
tot.len=ans.len=std::max(q-1,1);//直接计算所得数
if(q-1>1){ans[q-1]=1;ans[1]=8;for(int i=2;i<q-1;++i) ans[i]=9;}
if(q-1>0){tot[q-1]=9;}
if(ans+tot<x){ans=ans+tot;++cnt;b^=1;}
if(ans+tot<x){ans=ans+tot;++cnt;b^=1;}//根据加和次数判断此时前缀的奇偶性
q+=cnt/2-1;
tot[tot.len]=0;tot[tot.len=q]=1;
Bigint sum=x-ans;sum=sum+tot-Bigint(1);//根据上面的式子计算
if(b&1) sum=(sum<<(sum.len-1));//将前缀还原,分奇偶性讨论
else sum=(sum<<sum.len);
sum.reflectl();
return sum;
}
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
std::ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
for(cin>>t;t--;){
x.scan();n.scan();
get(n+rank(x)).print("\n");//当前排名 = 前面的回文数 + 中间的回文数
}
return 0;
}