問題の解決策
バージョン1:ブロック+ハッシュ
・ギャングスターを学ぶために、コードのソースを学ぶことを学びます
遮断部:
同じ分割B、(尾と呼ばれる)、及び4ビット(ヘッドAHという)5に前者、
、(最初のマイナス1)
ハッシュ部分:
もし
- 第暴力テールである0〜でハッシュテーブルに挿入され、その後、ああハッシュテーブルと一致するようにヘッドとして、クエリah0000〜A(AHAT)回答
- 暴力、その後、尾となるで+ 1 BT〜 (ハッシュ・テーブルは現在0〜BTの範囲である)ハッシュテーブルに挿入され、続いてBHハッシュテーブルに一致するように、ヘッダ、クエリbh0000〜B(bhbt)のよう答え
- 最後に、残りの部分BT + 1〜9999挿入(現在テーブルが9999から0までの範囲である)、列挙ああ〜(BH-1)ヘッドのようなすべての可能な答えを与えるためにah0000〜(BH-1) 9999
整頓は、統計結果ANS = +第三工程の第二段階である - 最初のステップ
もし すべて同じでしどこ等しいです
- 第暴力テールであるBT 0〜ハッシュテーブルに挿入され、次いでBHハッシュテーブル、クエリbh0000〜B(bhbt)回答と一致するようにヘッドとして
- 尾次いで暴力であるBT + 1〜に(ハッシュ・テーブルは現在0〜時の範囲である)ハッシュテーブルに挿入され、その後、ああハッシュテーブルに一致するヘッダとして、クエリに対する答えah0000〜A
- 最後に、残りの部分〜9999 + 1におけるヘッドとして全ての可能な答えを(現在のテーブルが9999から0までの範囲である)、列挙ああ〜(BH-1)挿入、与えることah0000〜(BH-1)
バージョン2:デジタルDP 申し訳ありませんが、最初の、そしてそう、私は徹底的に理解していませんでした
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
const int M=(1<<20)-1;
/*
这里帖一下上面链接的博客主的原话: 这真的是以现在的我的能力注意不到的地方呢
还有一些比较巧妙的地方就是:
1.用二进制来优化hash链表,用每一个数二进制下的后面20位来当第一维的下标,用每一个数的值来当第二位的下标,优化空间,也使得数组开的下
2.对于hash的操作,预先开设一块固定内存,避免在堆上申请内存和释放,加快速度
3.对于visit的数组的重用,每T组数据,某个值存在就将他标为visit[x]=T
4.就是莫队分块,分成头尾两部分--因为f(x)的可加性
*/
struct node{
ll key,cnt;
node* nxt;
}* g[M+1];//g[i]是二进制最后20位为i的链表的头指针
node* cur;
node memory[N];//预先定义一块内存空间 hash的操作都在此执行
ll visit[M+1];//第T组数据下 二进制最后20位为i的链表内有没有元素
ll T=0,k,s;
ll dt[10][20];//各种数字的各种次幂
void ins(ll x){
node* p;
if(x>s)return ;//超过计算界限 退出
ll u=x%M;//x二进制最后20位
if(visit[u]<T){//在第T组数据下 二进制最后20位为u的链表内没有元素就加入
//类似初始化了
visit[u]=T;
g[u]=NULL;
}
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;
for (p = g[u]; p ; p=p->nxt) {
if(p->key==x)return p->cnt;
}
return 0;
}
void init(){
//预处理各种数的各种次
for (int i = 0; i <= 9; ++i) {
dt[i][0]=1;
for (int j = 1; j <= 15; ++j) {
dt[i][j]=dt[i][j-1]*i;
}
}
}
void init_hash(){
T++;
cur=memory;//cur重新指向该内存的起始
}
ll cal(int x){//计算f(x,k)的值
ll res=0;
while(x){
res+=dt[x%10][k];
x/=10;
}
return res;
}
int main(){
ios::sync_with_stdio(0);
ll a,b;
int sqrt=10000;
ll ah,bh,at,bt;
ll reta,retb;
init();
while(cin>>a>>b>>k>>s){
init_hash();
reta=retb=0;
ah=(a-1)/sqrt;bh=b/sqrt;
at=(a-1)%sqrt;bt=b%sqrt;//预先处理处a和b的头部与尾部
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 <sqrt ; ++i) //将剩余的sqrt插入表中
ins(cal(i));
}else{
for (int i = 0; i <= bt; ++i)
ins(cal(i));//将尾部是0~bt的值插入表中
retb=ask(cal(bh));//得到头部是bh 尾部是0~bt的答案 即 bh*10000<= 数 <= b (bh*bt) 中的答案
for (int i = bt + 1; i <=at; ++i)
ins(cal(i));//再将尾部是bt+1~at的值插入表中
reta=ask(cal(ah));//得到 头部是ah 尾部是0~at的答案 即 ah*10000<= 数 <=a (ah*at)
for (int i = at + 1; i <sqrt; ++i)
ins(cal(i));
}
for (int i = ah; i < bh; ++i)
retb+=ask(cal(i));
//根据头部去匹配现在表中的值 求得 头部是ah<= <bh 尾部是0~sqrt-1 即 a-1<= 数 <b*10000 的答案
cout <<retb-reta << endl;
}
return 0;
}
達成に十分な、読んでいない、先制ピット、私は子供ビットソリューションは、DP停止し学ぶために戻って来るように
コードを学習情報源
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,k;
ll a,b,s;
ll pos[20];
ll f[20];//全排列
ll p[20][20];//各种数字的各种次方
//len 表示目前还有几位没有确定下来
//sum 表示要组合成的数
//top 目前在讨论的数字 先讨论9 再讨论8...直到0
ll dfs(int len,ll sum,int top){
if(len*p[top][k]<sum) return 0;//如果全部都是最高位加起来都没有总和大 直接返回0
if(sum<0)return 0;
if(top==0){
if(sum) return 0;
pos[0]=len;
ll res=f[n];
for (int i = 0; i <=9; ++i) {
res/=f[pos[i]];
//一个全排列就是符合要求的数的个数 因为首位是什么已经在solve()中的for里面决定好了
//所以后面首位是0页没关系 只要找到符合条件的数的个数 计算出他们可以组合出集中不同的个数就好了
}
return res;
}
ll res=0;
for (int i = 0; i <= len; ++i) {
pos[top]=i;//目前讨论的数字所在的位置
res+=dfs(len-i,sum-i*p[top][k],top-1);
}
return res;
}
//在dfs前先做一些处理 比如剪枝 以及记录dfs开始前有几位数还没有被确定下来
ll sub_solve(int len,ll sum){
if(len==0)return sum==0;
n=len;
return dfs(len,sum,9);
}
int len,bit[20];
ll solve(ll x){//求[1,x]内符合条件的数字的个数
len=0;
while(x){
bit[++len]=x%10;
x/=10;
}
ll res=0,sum=s;
for (int i = len; i ; --i) {
for (int j = 0; j <bit[i]; ++j) {
res+=sub_solve(i-1,sum-p[j][k]);
//目前讨论的数没有取到最大值(j没有等于bit[i]) 则后面的数可以随便取
}
sum-=p[bit[i]][k];//取到了最大值
}
res+=(sum==0);
return res;
}
int main(){
ios::sync_with_stdio(0);
//init
f[0]=1;//全排列
for (int i = 1; i <= 20; ++i) {
f[i]=i*f[i-1];
}
for (int i = 1; i < 10; ++i) {
p[i][0]=1;//i的各种次方
for (int j = 1; j < 16; ++j) {
p[i][j]=p[i][j-1]*i;
}
}
while(cin>>a>>b>>k>>s){
cout<<solve(b)-solve(a-1)<<endl;
}
return 0;
}