本来准备来数学题专题训练的,不过这次的题目都没太大思维量,没起到很好的效果。。。
在rqnoj的数论/数值 标签里看的题。。。基本成了高精度专题练习了。。。
题目一:wikioi1252 FIB词链
地址:http://www.wikioi.com/problem/1252/
这个嘛。。。假设g[n]为fib[n]包含的原单词数。。。其实g[n]就等于g[n-1]+g[n-2]+拼接后增加的部分。。。
然后就是怎么算这个增加部分了。。。假设要查询的串长度为len,其实只需要知道f[n-1]的后len-1个与f[n-2]的前len-1个字母。。。。所以每次都记下f[x]的前后一部分就好了。。。这个字符串处理还是比较麻烦。。。可以看下代码
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
int P=10000;
struct bign
{
int len,num[5000+10];
bign(){len=1;memset(num,0,sizeof(num));}
bign operator=(int x)
{
len=0;
while(x)
{
num[len]=x%P;
x/=P;
}
if(len==0)len=1;
return *this;
}
bign operator +(int x)
{
bign c;
c=*this;
c.num[0]+=x;
int temp=0;
for(int i=0;i<c.len;i++)
{
c.num[i]=c.num[i]+temp;
temp=c.num[i]/P;
c.num[i]%=P;
if(i==c.len-1&&temp!=0)c.len++;
}
return c;
}
bign operator +(const bign &b)const
{
bign c;
c.len=max(len,b.len);
int temp=0;
for(int i=0;i<c.len;i++)
{
c.num[i]=num[i]+b.num[i]+temp;
temp=c.num[i]/P;
c.num[i]%=P;
if(i==c.len-1&&temp!=0)c.len++;
}
return c;
}
void print()
{
printf("%d",num[len-1]);
for(int i=len-2;i>=0;i--)printf("%04d",num[i]);
printf("\n");
}
};
struct E
{
char fr[30+10],be[30+10];
bign num;
E(){memset(fr,0,sizeof(fr));memset(be,0,sizeof(be));}
}f[200+10];
int len;
char s[30+10];
int n;
char temp[60+10];
int getnum(int x)
{
int ret=0;
int t=0;
int len1=strlen(f[x-1].be);
int len2=strlen(f[x-2].fr);
for(int i=max(0,len1-len+1);i<len1;i++)temp[t++]=f[x-1].be[i];
for(int i=0;i<min(len1,len-1);i++)temp[t++]=f[x-2].fr[i];
if(t<len)return 0;
for(int i=0;i<t-len+1;i++)
{
bool ok=true;
for(int j=0;j<len;j++)
{
if(temp[i+j]!=s[j])
{
ok=false;
break;
}
}
if(ok)ret++;
}
return ret;
}
void copy(int x)
{
int t=0;
int len1=strlen(f[x-1].fr),len2=strlen(f[x-2].fr);
for(int i=0;i<len1;i++)f[x].fr[t++]=f[x-1].fr[i];
if(t<len)
for(int i=0;i<len2&&t<len;i++)
{
f[x].fr[t++]=f[x-2].fr[i];
}
if(t==len)t--,f[x].fr[t]='\0';
//----------------------------------------------
t=0;len1=strlen(f[x-1].be),len2=strlen(f[x-2].be);
if(len2<len-1)
{
for(int i=max(0,len1+len2-len+1);i<len1;i++)
{
f[x].be[t++]=f[x-1].be[i];
}
}
for(int i=0;i<len2&&t<len;i++)f[x].be[t++]=f[x-2].be[i];
if(t==len)t--,f[x].fr[t]='\0';
}
int main()
{
scanf("%s%d",s,&n);
len=strlen(s);
f[1].fr[0]=f[1].be[0]='b';
f[2].fr[0]=f[2].be[0]='a';
if(len==1)
{
if(s[0]=='a')f[2].num=1;
else f[1].num=1;
}
for(int i=3;i<=n;i++)
{
f[i].num=f[i-1].num+f[i-2].num+getnum(i);
copy(i);
}
f[n].num.print();
return 0;
}
题目二:rqnoj 4 数列
地址:http://www.rqnoj.cn/problem/4
这个题稍微和数学关系大点。。
对于题目中给的那个序列,其实很容易看出k^a 这一项刚好出现在2^a位置。。。
所以我们先找出最大的2^a<=要找的位置x。。。判断出最大的k^a是多少,加入答案。然后x-=2^a 继续判断第二,第三大的k^a是多少。。直到x为0,输出答案
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
using namespace std;
int ans=0;
int x;
int d;
int qpow(int x,int c)
{
if(c==0)return 1;
if(c==1)return x;
int ret=qpow(x,c/2);
ret=ret*ret;
if(c&1)ret=ret*d;
return ret;
}
int main()
{
scanf("%d%d",&d,&x);
if(x==1)cout<<1<<endl;
else
while(x>0)
{
int i,j;
for(i=1,j=0;i*2<=x;i<<=1,j++);
ans+=qpow(d,j);
x-=i;
}
cout<<ans<<endl;
return 0;
}
题目三:rqnoj 难题
地址:
http://www.rqnoj.cn/problem/139
这题还真是难(shui)啊。。。。就是裸的可重组合:公式c(n+m-1,n-1)...
然后用边乘边除的高精度从c(n+m-1,0)递推过来 (但实际数据范围比较小。。。不需要高精)
有个让我比较不解的地方就是算C(n+m-1,n-1)会挂一组。。。要算C(n+m-1,m)才对。。。明明都是一样的啊。。。有没有神牛给我解释下啊
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
using namespace std;
int n,m;
int P=1000;
struct bign
{
int num[1000+10],len;
bign()
{
memset(num,0,sizeof(num));
len=1;
}
bign operator =(int x)
{
len=0;
while(x)
{
num[len++]=x%P;
x/=P;
}
if(len==0)len=1;
return *this;
}
bign operator *(int x)
{
bign c;
c=*this;
int temp=0;
for(int i=0;i<c.len;i++)
{
c.num[i]*=x;
c.num[i]+=temp;
temp=c.num[i]/P;
c.num[i]%=P;
if(i==c.len-1&&temp!=0)c.len++;
}
return c;
}
bign operator /(int x)
{
bign c;
c.len=len;
int temp=0;
for(int i=c.len-1;i>=0;i--)
{
temp*=P;
temp+=num[i];
if(temp>=x)
{
c.num[i]=temp/x;
temp%=x;
}
}
while(c.len&&c.num[c.len-1]==0)c.len--;
if(c.len==0)c.len=1;
return c;
}
void print()
{
printf("%d",num[len-1]);
for(int i=len-2;i>=0;i--)printf("%03d",num[i]);
}
};
void C(int d,int c)
{
bign x;
x=1;
for(int i=1;i<=c;i++)
{
x=x*(d-i+1)/i;
}
x.print();
}
int main()
{
scanf("%d%d",&n,&m);
C(m+n-1,m);
return 0;
}
题目四:rqnoj 愚蠢的组合数
地址:http://www.rqnoj.cn/problem/29
就是判断组合数的奇偶性。。。学到了一个很重要的性质:当n&k==k时 C(n,k)为奇数,否则为偶数。。。
#include<cstdlib>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
int t,n,k;
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
if((n&k)==k)printf("1\n");
else printf("0\n");
}
return 0;
}