URAL 1309 Dispute

先简述一下题目吧:

f[0] = 0;

f[n] = g(n, f[n-1]);

g(x, y) = ((y-1)x^5 + x^3 -x*y + 3*x + 7*y) % 9973

如果直接去算,n<=10^8,那么时间一定是不够的。所以就是要加速计算,我感觉一定有周期性可以利用,但由于n的原因,我没能看出来周期性在哪里。

后面看了书上的题解。

先将f[n] = g(n, f[n-1])和g(x, y) = ((y-1)x^5 + x^3 -x*y + 3*x + 7*y) % 9973结合

f(x) =( (x^5 + x + 7)*y + (-x^5 + x^3 + 3*x) ) % 9973

令M = 9973

然后当x=n时和x=n-k*M时 

(x^5 + x + 7) % M 的值是一样的

(-x^5 + x^3 + 3*x) % M的值是一样的

所以存在一个周期的线性关系,既

f[x] = A * f[x-M] + B

f[x-M] = A * f[x - 2*M] + B

.......

f[n%M+M] = A * f[n%M] + B

(这一点我想了挺久,大家不明白的可以手动模拟着推一推就懂了)

所以就只要先算出f[n%M]

然后从n%M+1到n%M+M计算出A和B的值

然后根据f[n] = A * f[n-M] + B计算出f[n]

那么时间复杂度是O(n%M + [n / M]) 也就是10000左右就好了

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
const int M=9973;
int A = 1,B = 0;
int f[50000];
//计算a^t mod t 
int f_pow(int a, int t)
{
	int ans = 1,i;
	for (i=0; i<t; i++)
		ans = (ans*a) % M;
	return ans;
}
//将a模M 
void f_mol(int &a)
{
	a %= M;
	if (a < 0)
		a += M;
}
//计算x^5-x+7 
int f1(int x)
{
	int ans=0;
	ans = (f_pow(x, 5) - x + 7);
	f_mol(ans);
	return ans; 
}
//计算x^5+x^3+3x 
int f2(int x)
{
	int ans=0;
	ans = -1*(f_pow(x, 5)) + f_pow(x, 3) + 3*x;
	f_mol(ans);
	return ans;
}
int main()
{
	int i,j,n,m,l1,l2,ans;
	scanf("%d", &n);
	m = n % M;
	f[0] = 0;
	//直接计算出f[n%M]的值 
	for (i=1; i<=m; i++)
	{
		l1 = f1(i);
		l2 = f2(i);
		f[i] = l1*f[i-1] + l2;
		f_mol(f[i]);
	}
	//计算出f[t]=A*f[t-M]+B中的A和B的值 
	for (i=m+1; i<=m+M; i++)
	{
		l1 = f1(i);
		l2 = f2(i);
		f[i] = l1*f[i-1] + l2;
		A *= l1;
		B = B*l1 + l2;
		f_mol(f[i]);
		f_mol(A);
		f_mol(B);
	}
	//通过f[t]=A*f[t-M]+B,计算f[n%M],f[(n%M)+M],....,f[n-2*M],f[n-M],f[n]。 
	ans = f[m];
	while (m < n)
	{
		ans = A*ans+B;
		f_mol(ans);
		m += M;
	}
	printf("%d\n", ans);
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值