题目链接
题意:就是题中定义了一个函数,f(y,k)表示将y每一位的k次幂之和,f(y,k)-y=x,已知x,k求y的数量。
题解:对于这道题,开始的时候没有什么思路但是在看了题解之后豁然开朗。
我们可以判断的是假设y>10^10时,f(y,k)-y一定是小于0的,因为f(y,k)最大值就是109 ^ 9小于10 ^ 10,再往后面大依然是越来越小,所以y最大是10 ^ 10,所以我们可以在y的范围内对y进行操作。
下面重点来了!!!!我们可以把y分解成y=a+b1e5,我们可以发现任何小于1e10的数都可以这样来表式,这样我们就把a,b的范围控制在了1e5的可接受范围内。然后我们可以代入式子,f(a+b*1e5,k)-a-b 1e5=x ,因为a,b在书中控制的位不会产生交集,所以我们可以将原来的公式分解成f(a,k)+ f(b * 1e5,k)-a-b1e5,因为b * 1e5后面5位都是0,所以在函数中对函数值不做贡献可以删去,然后将等式移项,得:x-(f(a,k)-a)=f(b,k)-b * 1e5,这样,我们把需要的部分整理预处理出来,可以将两者中任意处理出来,然后看另一个在预处理中的数量,这里可以把(f(a,k)-a) 预处理出来,然后暴力b的值,看x-f(b,k)-b * 1e5在原数组中有多少个,这里有个小技巧,就是可以用upper_bound-lower_bound算出数量来降低复杂度。
下面是AC代码:
f[i][j]表示j^i是多少
g[i][j]表示j的各位平方和
//预处理
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
#define int long long
int f[10][10];
int g[100010][10];
int a[100100];
int n;
int cal(int y,int k)
{
int ans=0;
while(y)
{
ans+=f[y%10][k];
y/=10;
}
return ans;
}
void init()
{
for(int i=1;i<=9;i++)
{
for(int j=0;j<=9;j++)
{
f[j][i]=j;
for(int k=2;k<=i;k++) f[j][i]*=j;
}
}
//枚举y的各位数k方和
for(int i=1;i<=9;i++)//枚举k
{
for(int j=0;j<=100000;j++)//枚举y
{
g[j][i]=cal(j,i);
}
}
}
signed main()
{
init();
int t;
scanf("%lld",&t);
int cou=0;
while(t--)
{
int x,k;
int ans=0;
scanf("%lld%lld",&x,&k);
for(int i=1;i<=100000;i++) a[i]=g[i][k]-i;//枚举a
//两者不可同时为0,所以一个从1暴力,一个从0暴力
sort(a+1,a+100001);
for(int i=0;i<=100000;i++)
{
int t=g[i][k]-i*100000;
t=x-t;
ans+=upper_bound(a+1,a+100001,t)-lower_bound(a+1,a+100001,t);//这一点很妙,就是找相等的数
}
printf("Case #%lld: %lld\n",++cou,ans);
}
return 0;
}