数学相关专题1

本来准备来数学题专题训练的,不过这次的题目都没太大思维量,没起到很好的效果。。。

在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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值