Counting problem
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 630 Accepted Submission(s): 152
Problem Description
Supose that n has L digits, we used
d0,d1,d2,…,dL−1
to indicate every digit of n from least significant digit to most significant one.
f(n,k)=∑i=0L−1dki , for example f(305,2)=52+32+02=34
We are curious about that for each x ranged from a to b( a≤b ), how many integers make S=f(x,k) (here S is a constant) true.
f(n,k)=∑i=0L−1dki , for example f(305,2)=52+32+02=34
We are curious about that for each x ranged from a to b( a≤b ), how many integers make S=f(x,k) (here S is a constant) true.
Input
Multi test cases (about 100), every case gives four integers a, b, k, S in a single line.
Please process to the end of file.
[Technical Specification]
1≤a≤b≤999999999
1≤k≤15
1≤S≤1016
Please process to the end of file.
[Technical Specification]
1≤a≤b≤999999999
1≤k≤15
1≤S≤1016
Output
For each case,output a number in a single line indicates how many x ranged from a to b makes
S=f(x,k)
true.
Sample Input
1 1 1 1 1 10 2 1
Sample Output
1 2
Source
题意:
就是问你[a,b]区间内有多少x(a1a2a3...an),使得f(x)=a1^k+a2^k+a3^k+..+an^k=s
解析:
可以看大神博客上的解析,用了莫队分块的思想
https://www.cnblogs.com/bin-gege/p/5696092.html
还有一些比较巧妙的地方就是:
1.用二进制来优化hash链表,用每一个数二进制下的后面20位来当第一维的下标,用每一个数的值来当第二位的下标,优化空间,也使得数组开的下
2.对于hash的操作,预先开设一块固定内存,避免在堆上申请内存和释放,加快速度
3.对于visit的数组的重用,每T组数据,某个值存在就将他标为visit[x]=T
4.就是莫队分块,分成头尾两部分--因为f(x)的可加性
#include<cstdio>
#include<cstring>
typedef long long ll;
const int M = (1<<20)-1;
const int N = 1e4+10;
typedef struct node
{
ll key;
ll cnt; //与key相同的数的个数
node *nxt;
}node;
node *g[M+1],*cur; //g[i]是二进制最后20位为i的链表(一组key)的头指针
node memory[N]; //预先定义的一块内存空间,HASH的操作都在此执行
ll visit[M+1]; //在第T组数据下,二进制最后20位为i的链表内有没有元素
ll T=0,k,s;
ll dt[16][10];
void ins(ll x)
{
node *p;
if(x>s) return; //超过界限退出
ll u=x&M; //x二进制的最后20位
if(visit[u]<T) visit[u]=T,g[u]=NULL;//在第T组数据下,二进制最后20位为u的链表内没有元素,就加入
for(p=g[u];p;p=p->nxt) //在二进制最后20位为u的链表中找值为x的节点
{
if(p->key==x)
{
p->cnt++;
return;
}
}
p=cur++;p->key=x; //在在二进制最后20位为u的链表头部新建一个key为x的节点
p->cnt=1;p->nxt=g[u];
g[u]=p;
}
ll ask(ll x)
{
node *p;
if(x>s) return 0;
x=s-x;
ll u=x&M;
if(visit[u]<T) return 0; //在第T组数据下,二进制最后20位为u的链表内没有元素
for(p=g[u];p;p=p->nxt)
{
if(p->key==x)
{
return p->cnt;
}
}
return 0;
}
void init() //预处理num(0...9)^k(1...15)的值
{
for(int i=0;i<=9;i++)
{
for(int j=1;j<=15;j++)
{
if(j==1)
{
dt[j][i]=i;
}
else
{
dt[j][i]=dt[j-1][i]*i;
}
}
}
}
ll cal(ll x) //计算f(x,k)的值
{
ll res=0;
while(x)
{
res+=dt[k][x%10];
x=x/10;
}
return res;
}
void init_Hash()
{
T++;
cur=memory; //将cur重新指向该块内存的起始
}
int main()
{
ll a,b;
int sqr=10000;
ll ah,bh,at,bt;
ll reta,retb;
init();
while(scanf("%lld%lld%lld%lld",&a,&b,&k,&s)!=EOF)
{
//printf("****\n");
init_Hash();
reta=retb=0;
ah=(a-1)/sqr;bh=b/sqr; //处理出a,b的头部和尾部,因为后面要减a且题目是闭区间,所以a-1
at=(a-1)%sqr;bt=b%sqr;
if(at<bt)
{
for(int i=0;i<=at;i++) ins(cal(i)); //先将尾部是0-at的值插入表中
reta=ask(cal(ah)); //根据现在表中的值去跟头部匹配,求得数(头部是ah,尾部是0-at,即ah*10000<=数<=a-1)中的答案
for(int i=at+1;i<=bt;i++) ins(cal(i)); //将尾部是at+1-bt的值插入表中
retb=ask(cal(bh)); //根据现在表中的值去跟头部匹配,求得数(头部是bh,尾部是0-bt,即bh*10000<=数<=b)中的答案
for(int i=bt+1;i<sqr;i++) ins(cal(i)); //将剩余sqr中的值插入到表中
}
else
{
for(int i=0;i<=bt;i++) ins(cal(i));
retb=ask(cal(bh));
for(int i=bt+1;i<=at;i++) ins(cal(i));
reta=ask(cal(ah));
for(int i=at+1;i<sqr;i++) ins(cal(i));
}
for(int i=ah;i<bh;i++) retb+=ask(cal(i)); //根据头部去匹配现在表中的值,求得数(头部是ah<= <bh,尾部是0-sqr-1,即a-1<=数<bh*10000)中的答案
printf("%lld\n",retb-reta);
}
return 0;
}