Delta Wave UVA1478(组合数学+高精度C++)

Delta Wave

题目链接:https://vjudge.net/problem/UVA-1478

 

 

 

 

 

题解

剧毒题目(其实思维挺简单的,就是......)

分析一下,发现它的模型就是卡特兰数的经典模型,只不过中间有点变化

这道题其实就是选偶数个位置让它们组成一个括号序列(上走是左括号,下走是右括号,没有被选的位置就是平着走)

所以它的答案就是:

\sum_{k=1}^{\left \lfloor n/2 \right \rfloor}C_{n}^{2k}*Cat(k)其中Cat(k)为卡特兰数第k项

看了一下数据范围,咦?mod去哪儿了?

莫非是高精!(当场吐血)

 

还真是高精,n还小于等于10000,T小于等于20

那直接写组合数,高精除高精,岂不T飞。。。

还是先想一下怎么优化常数

我们把卡特兰数展开:

Cat(k)=\frac{C_{2k}^{k}}{k+1}

那么之前的答案就是:

\sum_{k=1}^{\left \lfloor n/2 \right \rfloor}\frac{C_{n}^{2k}*C_{2k}^{k}}{k+1}

考虑一下把相邻的两项做个比值(防止计算组合数时的阶乘预处理):

发现它们的比值竟然是长这样的:

r=\frac{C_{n}^{2k}*C_{2k}^{k}}{k+1}:\frac{C_{n}^{2(k-1)}*C_{2(k-1)}^{k-1}}{k}=\frac{(n-2k+1)*(n-2k+2)}{k*(k+1)}

所以我们就可以把高精除高精换成了高精乘低精+高精除低精

我们对于每个k只需要把上一项乘上(n-2k+1)*(n-2k+2)再除以k*(k+1)就可以啦

每算完一个值就把它累加到ans里面,这样就可以节省空间

 

写完运行,为什么一组10000就要跑1s???
突然发现自己忘了压位。。。

发现r的分母做多只有8位,而开long long可以存18位,所以我们可以压10位~~~

 

所以120ms就过了。。。

AC的代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const long long D=10000000000ll;
#define N 1005
#define LL long long
LL now[N],ans[N],tmp[N];
void jia(LL a[],int &lena,LL b[],int lenb)
{
	memset(tmp,0,sizeof(tmp));
	int len=min(max(lena,lenb),10),i;
	for(i=1;i<=len;i++)
		tmp[i]=a[i]+b[i];
	for(i=1;i<=len;i++)
		if(tmp[i]>=D){
			tmp[i+1]+=1ll*tmp[i]/D;
			tmp[i]%=D;
		}
	while(tmp[len+1]){
		len++;
		tmp[len+1]+=1ll*tmp[len]/D;
		tmp[len]%=D;
	}
	memcpy(a,tmp,sizeof(tmp));
	lena=len;
}
void cheng(LL a[],int &lena,int b)
{
	memset(tmp,0,sizeof(tmp));
	int len=lena,i;
	for(i=1;i<=len;i++)
		tmp[i]=1ll*a[i]*b;
	for(i=1;i<=len;i++)
		if(tmp[i]>=D){
			tmp[i+1]+=1ll*tmp[i]/D;
			tmp[i]%=D;
		}
	while(tmp[len+1]){
		len++;
		tmp[len+1]+=1ll*tmp[len]/D;
		tmp[len]%=D;
	}
	memcpy(a,tmp,sizeof(tmp));
	lena=len;
}
void chu(LL a[],int &lena,int b)
{
	memset(tmp,0,sizeof(tmp));
	int len=lena,i;
	long long t=0;
	for(i=len;i>=1;i--){
		tmp[i]=1ll*(1ll*D*t+1ll*a[i])/b;
		t=1ll*(1ll*D*t+1ll*a[i])%b;
	}
	while(!tmp[len])
		len--;
	memcpy(a,tmp,sizeof(tmp));
	lena=len;
}
int main()
{
	int lennow,lenans,k,n;
	while(~scanf("%d",&n)){
		memset(now,0,sizeof(now));
		memset(ans,0,sizeof(ans));
		memset(tmp,0,sizeof(tmp));
		now[1]=1;lennow=1;lenans=1;
		jia(ans,lenans,now,lennow);
		for(k=1;2*k<=n;k++){
			cheng(now,lennow,(n-2*k+1)*(n-2*k+2));
			chu(now,lennow,k*(k+1));
			jia(ans,lenans,now,lennow);
		}
		int flg=0,ff=0;
		for(k=min(lenans,10);k>=1;k--){
			if(ans[k])flg=1;
			if(flg){
				if(!ff)
					printf("%lld",ans[k]%D);
				else
					printf("%010lld",ans[k]);
			}
			ff=1;
		}
		printf("\n");
	}
}

 

 

过了样例就交,发现WA了,结果是输出的问题,输出时把每一位的数的前导零分别mod掉了。。。

结果调了我一个小时。。。

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值