・ブロック+ハッシュ/デジタルDP BC#15 / HDU 5085カウントの問題

問題の解決策

バージョン1:ブロック+ハッシュ
・ギャングスターを学ぶために、コードのソースを学ぶことを学びます

遮断部:
同じ分割B、(尾と呼ばれる)、及び4ビット(ヘッドAHという)5に前者、
A = A 時間 * 10000 + a t =ああ* 10000 +で 、(最初のマイナス1)
b = b h 10000 + b t B = BH * 10000 + BT

ハッシュ部分:

もし a t < b t で<BT

  1. 第暴力テールである0〜でハッシュテーブルに挿入され、その後、ああハッシュテーブルと一致するようにヘッドとして、クエリah0000〜A(AHAT)回答
  2. 暴力、その後、尾となるで+ 1 BT〜 ハッシュ・テーブルは現在0〜BTの範囲である)ハッシュテーブルに挿入され、続いてBHハッシュテーブルに一致するように、ヘッダ、クエリbh0000〜B(bhbt)のよう答え
  3. 最後に、残りの部分BT + 1〜9999挿入(現在テーブルが9999から0までの範囲である)、列挙ああ〜(BH-1)ヘッドのようなすべての可能な答えを与えるためにah0000〜(BH-1) 9999

整頓は、統計結果ANS = +第三工程の第二段階である - 最初のステップ
ここに画像を挿入説明

もし a t > b t > BTで すべて同じでしどこ等しいです

  1. 第暴力テールであるBT 0〜ハッシュテーブルに挿入され、次いでBHハッシュテーブル、クエリbh0000〜B(bhbt)回答と一致するようにヘッドとして
  2. 尾次いで暴力であるBT + 1〜に(ハッシュ・テーブルは現在0〜時の範囲である)ハッシュテーブルに挿入され、その後、ああハッシュテーブルに一致するヘッダとして、クエリに対する答えah0000〜A
  3. 最後に、残りの部分〜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;
}
公開された43元の記事 ウォンの賞賛0 ビュー1248

おすすめ

転載: blog.csdn.net/Yubing792289314/article/details/104254600