【HDU】5275 Dylans loves polynomial【拉格朗日插值法】

传送门:【HDU】5275 Dylans loves polynomial

题目分析:首先,队友给了我一个拉格朗日插值法的公式,裸的OWO,如下:
∑ i = l r { y i ∏ i ! = j ( x i − x j ) ∏ i ! = j ( x − x j ) } \sum_{i=l}^{r}\{\frac{y_i}{\prod_{i!=j}(x_i-x_j)}\prod_{i!=j}(x-x_j)\} i=lr{i!=j(xixj)yii!=j(xxj)}
只要把题目给的坐标带入 x i , x j , y i , x x_i,x_j,y_i,x xi,xj,yi,x,就可以求出答案了……
然后我们要怎么求呢?首先要求 x i − x j x_i-x_j xixj的逆元,因为最多只有500000种,且分布在[-250000,250000],且关于y轴 − x -x x的逆元和 x x x的逆元互为相反数,于是我们只用求[1,250000]内的逆元即可。
然后对于 ∏ i ! = j ( x i − x j ) \prod_{i!=j}(x_i-x_j) i!=j(xixj),我们可以 O ( N 2 ) O(N^2) O(N2)预处理。然后每次查询, ∏ i ! = j ( x − x j ) \prod_{i!=j}(x-x_j) i!=j(xxj)是可以预先算的,因为 ∏ i ! = j ( x − x j ) = ∏ ( x − x j ) x − x i \prod_{i!=j}(x-x_j)=\frac{\prod(x-x_j)}{x-x_i} i!=j(xxj)=xxi(xxj),这里我们需要注意的一点是, x − x i x-x_i xxi可能为0,此时没有逆元,这时候因为 x i x_i xi各不相同,于是 x − x i x-x_i xxi等于0的项最多出现一次,这时候我们暴力算就行了。

于是总复杂度为 O ( N 2 + N Q ) O(N^2+NQ) O(N2+NQ)

m y    c o d e : my~~code: my  code:

#include <bits/stdc++.h>
using namespace std ;

typedef long long LL ;

#define clr( a , x ) memset ( a , x , sizeof a )
#define cpy( a , x ) memcpy ( a , x , sizeof a )

const int MAXN = 3005 ;
const int MAXM = 250005 ;
const int mod = 1e9 + 7 ;

int mul[MAXN][MAXN] ;
int inv[MAXM] ;
int p[MAXN][2] ;
int n , q ;

int power ( int a , int b ) {
	LL res = 1 , tmp = a ;
	for ( ; b ; b >>= 1 , tmp = tmp * tmp % mod ) {
		if ( b & 1 ) res = res * tmp % mod ;
	}
	return res ;
}

void preprocess () {
	for ( int i = 1 ; i <= n ; ++ i ) {
		mul[i][i] = 1 ;
		for ( int j = i + 1 ; j <= n ; ++ j ) {
			mul[i][j] = ( LL ) mul[i][j - 1] * inv[abs ( p[i][0] - p[j][0] )] % mod * ( p[i][0] < p[j][0] ? -1 : 1 ) ;
			if ( mul[i][j] < 0 ) mul[i][j] += mod ;
		}
		for ( int j = i - 1 ; j >= 1 ; -- j ) {
			mul[i][j] = ( LL ) mul[i][j + 1] * inv[abs ( p[i][0] - p[j][0] )] % mod * ( p[i][0] < p[j][0] ? -1 : 1 ) ;
			if ( mul[i][j] < 0 ) mul[i][j] += mod ;
		}
	}
}

void solve () {
	int l , r , x ;
	for ( int i = 1 ; i <= n ; ++ i ) {
		scanf ( "%d%d" , &p[i][0] , &p[i][1] ) ;
	}
	preprocess () ;
	scanf ( "%d" , &q ) ;
	for ( int i = 1 ; i <= q ; ++ i ) {
		scanf ( "%d%d%d" , &l , &r , &x ) ;
		int res = 0 , tot = 1 ;
		for ( int j = l ; j <= r ; ++ j ) {
			tot = ( LL ) tot * ( x - p[j][0] ) % mod ;
		}
		for ( int j = l ; j <= r ; ++ j ) {
			int tmp = ( LL ) p[j][1] * mul[j][l] % mod * mul[j][r] % mod ;
			if ( x != p[j][0] ) tmp = ( LL ) tmp * tot % mod * inv[abs ( x - p[j][0] )] % mod * ( x < p[j][0] ? -1 : 1 ) ;
			else {
				int t = 1 ;
				for ( int k = l ; k <= r ; ++ k ) {
					if ( k != j ) t = ( LL ) t * ( x - p[k][0] ) % mod ;
				}
				tmp = ( LL ) tmp * t % mod ;
			}
			if ( tmp < 0 ) tmp += mod ;
			res = ( res + tmp ) % mod ;
		}
		printf ( "%d\n" , res ) ;
	}
}

int main () {
	inv[0] = 1 ;
	for ( int i = 1 ; i < MAXM ; ++ i ) {
		inv[i] = power ( i , mod - 2 ) ;
	}
	while ( ~scanf ( "%d" , &n ) ) solve () ;
	return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值