Manthan, Codefest 17 E. Salazar Slytherin's Locket 数位DP

原题链接:http://codeforces.com/contest/855/problem/E

题目大意:一个数字在B进制下为魔法数字当且仅当其在B进制下,0到B-1在其数位上均出现偶数次。问从L到R的B进制下魔法数字的数量。(L<R<10^18 , 2<=B<=10)

首先数位DP的框架大多是不会变的,重点在于如何求已知前a个数位上的数字,可以使得其为魔法数字的后面b个未知的数位上的数字排布方案数。

假设前面a个数位中有sum个数字出现了奇数次,于是对于这sum个数字就需要在后面各出现奇数次,如果没有填充完后面的所有数位,则需要继续填充除了sum个数字外的数字,同时再次填充的数字必须各自出现偶数次才能使其为魔法数字。

因为数位的数量比较少,可以直接暴力枚举出现的次数。

最后由于多组询问,加上记忆化以后就可以AC了。

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline void read(int &x){
    char ch;
    bool flag=false;
    for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
    x=flag?-x:x;
}


inline void read(long long &x){
    char ch;
    bool flag=false;
    for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
    x=flag?-x:x;
}

inline void write(int x){
    static const int maxlen=100;
    static char s[maxlen];
        if (x<0) {   putchar('-'); x=-x;}
    if(!x){ putchar('0'); return; }
    int len=0; for(;x;x/=10) s[len++]=x % 10+'0';
    for(int i=len-1;i>=0;--i) putchar(s[i]);
}

inline void write(long long x){
    static const int maxlen=100;
    static char s[maxlen];
        if (x<0) {   putchar('-'); x=-x;}
    if(!x){ putchar('0'); return; }
    int len=0; for(;x;x/=10) s[len++]=x % 10+'0';
    for(int i=len-1;i>=0;--i) putchar(s[i]);
}
long long  b,l,r;
long long P[20];
long long f[ 300][ 20][20][20];
long long C[ 300][300];
map< pair<int, long long>  , long long > M;

long long get_C(int n ,int m){
if ( C[ n][m]!=-1)
	return C[n][m];
long long ans=1;
for (int i=1;i<=m;i++)
	ans=ans*(n-i+1)/i;
C[n][m]=ans;
return ans;
}


long long get_num(int wei,int tot,int tmp,int cnt){
if ( wei < tot )
	return 0;
if ( cnt > tmp )
	return 0;
if ( ( wei==0 )&& ( tot==0) )
	return get_C( tmp , cnt );
if ( f[ wei ][ tot ][ tmp ][ cnt ]!=-1)
	return f[ wei ][ tot ][ tmp ][ cnt ];
long long ans=0;
if (tot>0)
	{
		for (int i=1;i<=wei-tot+1;i+=2)
			ans+=get_C( wei , i )*get_num( wei-i , tot-1 , tmp,cnt);
	}
else
	{
		for (int i=2;i<=wei;i+=2)
			ans+=get_C( wei , i )*get_num( wei-i , tot , tmp , cnt+1);
	}
f[ wei ][ tot ][ tmp ][ cnt ]=ans;
return ans;
}

long long get_ans(int b,long long  x){
if ( M.count( make_pair( b , x ) ) )
	return M[ make_pair( b , x ) ];
if (x==0)
	return 1;
unsigned long long tmp=1;
long long wei=1;
while ( tmp*b<= x)
    {
        tmp=tmp*b;
        wei++;
    }
if ( wei%2==1 )
	return get_ans( b , tmp-1 );
long long ans=get_ans(b,tmp-1);
long long now=0;
long long sum=0;
bool op=1;
long long TMPX=x;
while ( wei )
    {
    	if ( sum>wei)
    		break;
        for (int i=op;i<x/tmp;i++)
        {
            if ( ( ( 1<<i )&now )==0)
            	sum++;
            else
        		sum--;
            ans+=get_num( wei-1 , sum ,b-sum,0);
            if ( ( ( 1<<i ) &now )==0)
            	sum--;
            else
        		sum++;
        }
        if (  (  now & ( ( 1<< (x/tmp) ) )  )==0 )
            sum++;
        else
    		sum--;
        now=now^( ( 1<< (x/tmp) ) );
        x=x%tmp;
        tmp=tmp/b;
        op=0;
        wei--;
    }
if ( sum==0 )
	ans++;
M[ make_pair(b,TMPX) ]=ans;
return ans;
}


void doit(){
read(b); read(l);read(r);
long long tmp=get_ans(b,r)-get_ans(b,l-1);
write(tmp);
puts("");
}


int main(){
	//freopen("a.out","w",stdout);
	memset(f,-1,sizeof(f));
	memset(C,-1,sizeof(C));
    int T;
    read(T);
    for (int I=1;I<=T;I++)
	    doit();
    return 0;
}


猜你喜欢

转载自blog.csdn.net/u012602144/article/details/78086894