UVa 10213 - How Many Pieces of Land ?

题目:一个椭圆形的平面,在边界上去n个点做出它的内接n变形,联结所有的顶点,问最多把平面分成几块。

分析:数学题、大整数。地推求公式,然后大整数模拟。

            

            如图,n为1、2、3的情况为上面的小圆,下面的大圆为在n-1的情况下,加入第n个点的状态;

            1.计算增量:

            图中取第n个点与第i个点连接的状态,这条线将点分成2个集合(除去i和n):【1,i-1】,【i+1,n-1】;

            这两个集合中的任意两点连线都会与i-n相交,各自内部的点都不会与i-n相交,共(i-1)*(n-1-i)个交点;

            存在k个交点,则分i-n为k+1段,这k+1段线段将各自经过的区域一分为2,所以增加了k+1个区域;

            下面,求n与所有顶点连线后增加的区域总数:f(n)= Σ【(i-1)*(n-1-i)+ 1】,i 取值【1,n-1】;

             看着有点复杂,不过Σi*(n-i+1)= n*(n+1)*(n+2)/ 6,可以用数学归纳法证明;

             因此:f(n)= (n-1)*(n^2 - 5n + 12)/ 6,这个公式对于n≥2都成立,其中f(1)= 0,也认为成立;

             2.计算总数:

             不过 f(n)是增量的公式,我们要的结果是:g(n)= g(1)+ Σ f(i),i 取值【1,n】,g(1)= 1;

             将g(n)的公式展开成0次、1次、2次、3次的和式,然后利用各自的求和公式计算,整理得到结果:

             g(n)= 1 - 2n + (30 -7n + n^2)* n*(n+1)/ 24;

            数据范围很大,使用long long数组模拟计算防止溢出;

说明:这里有个坑,n = 2^31-1时+1后会溢出,所以需要将n转化成long long类型再计算。

#include <cstdlib>
#include <cstdio>

#define out_format(s1,w,s2) #s1#w#s2


const int big_max_size = 10;             // length
const long long big_base = 100000000LL;  // base
const char *big_print_format = "%08lld"; // width

long long ans[big_max_size+1];
long long buf[big_max_size+1];

void big_zero(long long a[])
{
	for (int i = 0; i < big_max_size; ++ i) {
		a[i] = 0;
	}
}

void big_one(long long a[])
{
	big_zero(a);
	a[0] = 1LL;
}

void big_carry(long long a[])
{
	for (int i = 0; i < big_max_size; ++ i) {
		if (a[i] >= big_base) {
			a[i+1] += a[i]/big_base;
			a[i]   %= big_base;
		}
	}
}

void big_add(long long a[], long long b[]) // a = a + b
{
	for (int i = 0; i < big_max_size; ++ i) {
		a[i] += b[i];
	}
	big_carry(a);
}

void big_sub(long long a[], long long b[]) // a = a - b
{
	for (int i = 0; i < big_max_size; ++ i) {
		a[i] -= b[i];
		if (a[i] < 0LL) {
			a[i+1] -= 1;
			a[i]   += big_base;
		}
	}
	big_carry(a);
}

void big_mul(long long a[], long long value) // a = a * b
{
	for (int i = 0; i < big_max_size; ++ i) {
		a[i] *= value;
	}
	big_carry(a);
}

void big_div(long long a[], long long value) // a = a / b
{
	for (int i = big_max_size-1; i > 0; -- i) {
		a[i-1] += a[i]%value*big_base;
		a[i]   /= value;
	}
	a[0] /= value; // if (a[0]%value != 0) there is something wrong
}

void big_print(long long a[])
{
	int end = big_max_size-1;
	while (!a[end] && end > 0) {
		-- end;
	}
	printf("%lld",a[end --]);
	while (end >= 0) {
		printf(big_print_format,a[end --]); 
	}   // if big_base != 10 should change the format
	puts("");
}

int main()
{
	int s, n;
	while (~scanf("%d",&s)) {
		for (int i = 0; i < s; ++ i) {
			scanf("%d",&n);
			big_one(ans); // n*n*n*(n+1) 
			big_mul(ans, n);
			big_mul(ans, n);
			big_mul(ans, n);
			big_mul(ans, n+1LL);
			big_one(buf); // 7*n*n*(n+1)
			big_mul(buf, 7);
			big_mul(buf, n);
			big_mul(buf, n);
			big_mul(buf, n+1LL);
			big_sub(ans, buf);
			big_one(buf); // 30*n*(n+1)
			big_mul(buf, 30);
			big_mul(buf, n);
			big_mul(buf, n+1LL);
			big_add(ans, buf);
			big_div(ans, 24);
			big_one(buf); // 2n
			big_mul(buf, 2);
			big_mul(buf, n);
			big_sub(ans, buf);
			big_one(buf); // 1
			big_add(ans, buf);
			big_print(ans);
		}
	}
	
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值