2^k进制数 vijos1315 NOIP2006提高组T4

题面在最下方。

从题意中可以获取灵感:k与w是有关系的。

当这个2^k进制数转化为2进制时,原来的每一位(可以相对于十进制中的每一位来理解)都对应了二进制数中长为k的一个区段。

举个栗子,对于一个16(2^4)进制的数 ABCD (即 10 11 12 13)

打开windows自带的计算器就能看到:,其中 A对应二进制下的1010,B对应1011,以此类推。

所以这就体现出了阶段性的特点,可以考虑采用DP解答。 

首先要做的准备工作:

根据题意“在2^k进制下的这个数的位数要大于等于2,数字严格上升”,所以不可能有任何有效位取0的情况(可以有前导零)。

设当前推导位(第i位)取值范围为[1,maxx],令x=w%n,对于maxx讨论如下:

若 x==0 , 则所有位的取值范围都是[1,(1<<k)-1]。

若 x!=0 ,则在最高位上可取的值为 [1,(1<<x)-1],其他位是[1,(1<<k)-1]。

从低位向高位推导,令 F[i][j] 是第i位上放数字j后满足条件的总方案数

则转移方程为 F[i][j]=∑F[i-1][k](j<k<=maxx)

同时由于w限制的是最大位数而非必需位数,所以在每次转移过后,都要 ans+=F[i][j]

如何优化?

第一个简单的优化,每一次状态转移的求和可以用后缀数组进行优化。

第二个优化是可以用滚动数组的思想,把F[i][j]优化为一维的F[i],具体可看代码实现。

其他要说的

高精度的模版是我从网上扒下来的,至今不会写高精度(逃)

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<string>
  5 using namespace std;
  6 template<class T> inline void read(T &_a){
  7     bool f=0;int _ch=getchar();_a=0;
  8     while(_ch<'0' || _ch>'9'){if(_ch=='-')f=1;_ch=getchar();}
  9     while(_ch>='0' && _ch<='9'){_a=(_a<<1)+(_a<<3)+_ch-'0';_ch=getchar();}
 10     if(f)_a=-_a;
 11 }
 12 const int maxn = 300;    
 13     
 14 struct bign{    
 15     int d[maxn], len;    
 16     void clean() { while(len > 1 && !d[len-1]) len--; }    
 17     
 18     bign()    { memset(d, 0, sizeof(d)); len = 1; }    
 19     bign(int num)     { *this = num; }     
 20     bign(char* num) { *this = num; }    
 21     bign operator = (const char* num){    
 22     memset(d, 0, sizeof(d)); len = strlen(num);    
 23     for(int i = 0; i < len; i++) d[i] = num[len-1-i] - '0';    
 24     clean();    
 25     return *this;    
 26     }    
 27     bign operator = (int num){    
 28     char s[20]; sprintf(s, "%d", num);    
 29     *this = s;    
 30     return *this;    
 31     }    
 32     
 33     bign operator + (const bign& b){    
 34     bign c = *this; int i;    
 35     for (i = 0; i < b.len; i++){    
 36     c.d[i] += b.d[i];    
 37     if (c.d[i] > 9) c.d[i]%=10, c.d[i+1]++;    
 38     }    
 39     while (c.d[i] > 9) c.d[i++]%=10, c.d[i]++;    
 40     c.len = max(len, b.len);    
 41     if (c.d[i] && c.len <= i) c.len = i+1;    
 42     return c;    
 43     }    
 44     bign operator - (const bign& b){    
 45     bign c = *this; int i;    
 46     for (i = 0; i < b.len; i++){    
 47     c.d[i] -= b.d[i];    
 48     if (c.d[i] < 0) c.d[i]+=10, c.d[i+1]--;    
 49     }    
 50     while (c.d[i] < 0) c.d[i++]+=10, c.d[i]--;    
 51     c.clean();    
 52     return c;    
 53     }    
 54     bign operator * (const bign& b)const{    
 55     int i, j; bign c; c.len = len + b.len;     
 56     for(j = 0; j < b.len; j++) for(i = 0; i < len; i++)     
 57     c.d[i+j] += d[i] * b.d[j];    
 58     for(i = 0; i < c.len-1; i++)    
 59     c.d[i+1] += c.d[i]/10, c.d[i] %= 10;    
 60     c.clean();    
 61     return c;    
 62     }    
 63     bign operator / (const bign& b){    
 64     int i, j;    
 65     bign c = *this, a = 0;    
 66     for (i = len - 1; i >= 0; i--)    
 67     {    
 68     a = a*10 + d[i];    
 69     for (j = 0; j < 10; j++) if (a < b*(j+1)) break;    
 70     c.d[i] = j;    
 71     a = a - b*j;    
 72     }    
 73     c.clean();    
 74     return c;    
 75     }    
 76     bign operator % (const bign& b){    
 77     int i, j;    
 78     bign a = 0;    
 79     for (i = len - 1; i >= 0; i--)    
 80     {    
 81     a = a*10 + d[i];    
 82     for (j = 0; j < 10; j++) if (a < b*(j+1)) break;    
 83     a = a - b*j;    
 84     }    
 85     return a;    
 86     }    
 87     bign operator += (const bign& b){    
 88     *this = *this + b;    
 89     return *this;    
 90     }    
 91     
 92     bool operator <(const bign& b) const{    
 93     if(len != b.len) return len < b.len;    
 94     for(int i = len-1; i >= 0; i--)    
 95     if(d[i] != b.d[i]) return d[i] < b.d[i];    
 96     return false;    
 97     }    
 98     bool operator >(const bign& b) const{return b < *this;}    
 99     bool operator<=(const bign& b) const{return !(b < *this);}    
100     bool operator>=(const bign& b) const{return !(*this < b);}    
101     bool operator!=(const bign& b) const{return b < *this || *this < b;}    
102     bool operator==(const bign& b) const{return !(b < *this) && !(b > *this);}    
103     
104     string str() const{    
105     char s[maxn]={};    
106     for(int i = 0; i < len; i++) s[len-1-i] = d[i]+'0';    
107     return s;    
108     }    
109 };
110 
111 int k,w,deg,maxx;
112 bign f[1<<10],sum[1<<10],ans;
113 
114 int main()
115 {
116     read(k); read(w);
117     deg=(1<<k)-1;
118     for (register int i=0;i<=deg;++i) f[i]=1;
119     while(w>=k)
120     {
121         w-=k;
122         if(w>=k) maxx=deg;
123             else maxx=(1<<w)-1;
124         for (register int i=deg;i;--i) sum[i]=sum[i+1]+f[i];
125         for (register int i=1;i<=maxx;++i) f[i]=sum[i+1],ans+=f[i];
126     }
127     printf("%s",ans.str().begin());
128     return 0;
129 }
View Code

描述

设r是个2^k 进制数,并满足以下条件:
(1)r至少是个2位的2^k 进制数。

(2)作为2^k 进制数,除最后一位外,r的每一位严格小于它右边相邻的那一位。

(3)将r转换为2进制数q后,则q的总位数不超过w。

在这里,正整数k(1≤k≤9)和w(k<W≤30000)是事先给定的。

问:满足上述条件的不同的r共有多少个?
我们再从另一角度作些解释:设S是长度为w 的01字符串(即字符串S由w个“0”或“1”m组成),S对应于上述条件(3)中的q。将S从右起划分为若干个长度为k 的段,每段对应一位2^k进制的数,如果S至少可分成2段,则S所对应的二进制数又可以转换为上述的2^k 进制数r。

例:设k=3,w=7。则r是个八进制数(23=8)。由于w=7,长度为7的01字符串按3位一段分,可分为3段(即1,3,3,左边第一段只有一个二进制位),则满足条件的八进制数有:
2位数:高位为1:6个(即12,13,14,15,16,17),高位为2:5个,…,高位为6:1个(即67)。共6+5+…+1=21个。

3位数:高位只能是1,第2位为2:5个(即123,124,125,126,127),第2位为3:4个,…,第2位为6:1个(即167)。共5+4+…+1=15个。

所以,满足要求的r共有36个。

格式

输入格式

输入文件只有1行,为两个正整数,用一个空格隔开:
k W

输出格式

输出文件为1行,是一个正整数,为所求的计算结果,即满足条件的不同的r的个数(用十进制数表示),要求最高位不得为0,各数字之间不得插入数字以外的其他字符(例如空格、换行符、逗号等)。

(提示:作为结果的正整数可能很大,但不会超过200位)

样例1

样例输入1

3 7

样例输出1

36

限制

1s

原文地址:https://www.cnblogs.com/jaywang/p/7689753.html