hdu_5085_Counting problem(莫隊分塊思想)

題目連接:hdu_5085_Counting problem

題意:給你一個計算公式,然後給你一個區間,問這個區間內滿足條件的數有多少個

題解:由於這個公式比較特殊,具有可加性,我們考慮講一個數分爲兩個部分,這樣就可以用莫隊的思想均攤時間複雜度,將9位數分爲一個4位和一個5位,這裏我感覺sqr爲10000 速度比較快。然後如果b小於sqr,那麼直接暴力就行,如果b大於sqr,那麼我們要把a和b都分爲頭部和尾部(注意是閉區間,a需要減1),如果a小於sqr,那麼他的頭部就爲0,然後計算0-a的尾部,並將相應的值插入Hash表,然後計算以a頭部開頭滿足條件的數,記爲reta,同理,計算0-b的尾部,記爲retb,如果b尾小於a尾,就要先計算b再計算a,然後將0到sqr-1的數對應的值也全部插入Hash表,然後從a的頭到b的頭,尋找對應的值,這裏要用一下容斥定理,retb-reta就是從[a,b]之間滿條件的數的個數.


#include<cstdio>
#define F(i,a,b) for(int i=a;i<=b;i++)
typedef long long LL;

const LL M=(1<<20)-1,N=1e5+7,sqr=1e4;
LL K,a,b,ahd,bhd,bt,at,reta,retb,T,S,dt[10][16];
inline LL pow(LL x,LL k){
	LL an=1;
	while(k){
		if(k&1)an*=x;
		k>>=1,x*=x;
	}
	return an;
}
//------------Hash table------------------------
struct E{LL key;LL cnt;E *nxt;}*g[M+1],pool[N],*cur=pool,*p;LL vis[M+1];
void init_Hash(){T++,cur=pool;}
inline void ins(LL key){
	if(key>S)return;
	LL u=key&M;
	if(vis[u]<T)vis[u]=T,g[u]=NULL;
	for(p=g[u];p;p=p->nxt)if(p->key==key){p->cnt++;return;}
	p=cur++,p->key=key,p->cnt=1,p->nxt=g[u],g[u]=p;
}

inline LL ask(LL key){
	if(key<0)return 0;
	LL u=key&M;
	if(vis[u]<T)return 0;
	for(p=g[u];p;p=p->nxt)if(p->key==key)return p->cnt;
	return 0;
}
//----------------------------------------------
inline LL cal(LL x){
	LL an=0;
	while(x)an+=dt[x%10][K],x/=10;
	return an;
}
void init(){F(i,0,9)F(j,1,15)dt[i][j]=pow(i,j);}
int main(){
	init();
	while(~scanf("%lld%lld%lld%lld",&a,&b,&K,&S)){
		init_Hash();
		ahd=(a-1)/sqr,bhd=b/sqr,at=(a-1)%sqr,bt=b%sqr;
		if(at<bt){
		F(i,0,at)ins(cal(i));
		reta=ask(S-cal(ahd));
		F(i,at+1,bt)ins(cal(i));
		retb=ask(S-cal(bhd));
		F(i,bt+1,sqr-1)ins(cal(i));
	}else{
		F(i,0,bt)ins(cal(i));
		retb=ask(S-cal(bhd));
		F(i,bt+1,at)ins(cal(i));
		reta=ask(S-cal(ahd));
		F(i,at+1,sqr-1)ins(cal(i));
	}
	F(i,ahd,bhd-1)retb+=ask(S-cal(i));
	printf("%lld\n",retb-reta);
	}
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章