HDU - 6253 Knightmare (打表+拉格朗日插值)

题目链接

题意:一个马在无限大的棋盘中跳,问跳n步能跳到多少个不同的格子。

首先写个打表程序打一下n比较小的时候的表:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N=50+10,mod=998244353;
 5 const int dx[]= {-2,-1,1,2,2,1,-1,-2};
 6 const int dy[]= {-1,-2,-2,-1,1,2,2,1};
 7 typedef pair<int,int> P;
 8 set<P> st[2];
 9 int n=20,a[N];
10 int main() {
11     st[0].insert({0,0});
12     for(int i=0; i<n; ++i) {
13         for(P p:st[i&1]) {
14             st[i&1^1].insert(p);
15             for(int j=0; j<8; ++j)st[i&1^1].insert({p.first+dx[j],p.second+dy[j]});
16         }
17         a[i]=st[i&1].size();
18     }
19     for(int i=0; i<n; ++i)printf("%d ",a[i]);
20     return 0;
21 }

打印结果:

1 9 41 109 205 325 473 649 853 1085 1345 1633 1949 2293 2665 3065 3493 3949 4433 4945

把元素差分两次后,成了这个亚子:

1 7 24 36 28 24 28 28 28 28 28 28 28 28 28 28 28 28 28 28

发现了什么?当n比较大的时候,经过二次差分后的数组的每一项都是28!因此可以猜测答案是一个关于n的二次多项式,现在要做的是把这个多项式推出来。

手算当然可以,但有没有一个可以不用动脑子就算出来的代码吗?答案是肯定的。拉格朗日插值大法好!

核心代码:(只需要写三个函数,前两个函数的作用是封装多项式的加法和乘法,第三个函数的作用是插值)

 1 typedef double db;
 2 typedef vector<db> Poly;
 3 Poly operator*(Poly a,Poly b) {
 4     Poly c;
 5     c.resize(a.size()+b.size()-1);
 6     for(int i=0; i<a.size(); ++i)
 7         for(int j=0; j<b.size(); ++j)c[i+j]+=a[i]*b[j];
 8     return c;
 9 }
10 Poly operator+(Poly a,Poly b) {
11     Poly c;
12     c.resize(max(a.size(),b.size()));
13     for(int i=0; i<c.size(); ++i) {
14         if(i<a.size())c[i]+=a[i];
15         if(i<b.size())c[i]+=b[i];
16     }
17     return c;
18 }
19 Poly Lagrange(Poly X,Poly Y) {
20     Poly c;
21     int n=X.size();
22     for(int i=0; i<n; ++i) {
23         Poly x({1});
24         for(int j=0; j<n; ++j)if(j!=i) {
25                 x=x*Poly({-X[j],1});
26                 x=x*Poly({1.0/(X[i]-X[j])});
27             }
28         c=c+x*Poly({Y[i]});
29     }
30     return c;
31 }

这样一来,只要输入X向量和Y向量,就能直接求出原多项式了,非常方便。比如输入如下两个向量:

1     Poly a({1,2,3}),b({1,3,7});
2     Poly c=Lagrange(a,b);
3     for(db x:c)printf("%f ",x);

输出的结果为

1.000000 -1.000000 1.000000

也就是说,三个点$(1,2),(2,3),(3,7)$所确定的多项式为$f(x)=x^2-x+1$

现在我们在打印的结果中任取三个点比如$(10,1345),(11,1633),(12,1949)$,得到的结果为:

5.000000 -6.000000 14.000000

即答案关于n的多项式为$f(n)=14n^2-6n+5$。当n比较大时的答案就可以通过这个式子算出来了,n比较小的时候直接输出结果即可。最终提交上去的代码应该是这个亚子:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef unsigned long long ll;
 4 const int N=1000+10,inf=0x3f3f3f3f;
 5 const int a[]= {1,9,41,109,205,325,473,649,853,1085,1345};
 6 int ka,n;
 7 int main() {
 8     int T;
 9     for(scanf("%d",&T); T--;) {
10         printf("Case #%d: ",++ka);
11         scanf("%d",&n);
12         if(n<=10)printf("%d\n",a[n]);
13         else printf("%llu\n",5-(ll)6*n+(ll)14*n*n);
14     }
15     return 0;
16 }

注意要用unsigned long long,OK了~

原文地址:https://www.cnblogs.com/asdfsag/p/11182035.html

时间: 2024-12-11 12:46:21

HDU - 6253 Knightmare (打表+拉格朗日插值)的相关文章

hdu 6253 (bfs打表)

链接:http://acm.hdu.edu.cn/showproblem.php?pid=6253 题意: 马可以往一个方向走两步,然后转个弯走一步,这样算一次动作,求问马n次动作后,能到达多少个点,重复到达的点只算一次. 思路: 一开始完全没思路,画图找了半天把自己画崩了,后面看到数据和样例感觉这应该是一道公式题,然后打了一个表.. 打表代码: #include<bits/stdc++.h> using namespace std; #define ull unsigned long lon

【数值分析】拉格朗日插值与牛顿插值

在工程应用和科学研究中,经常要研究变量之间的关系y=f(x).但对于函数f(x),常常得不到一个具体的解析表达式,它可能是通过观测或实验得到的一组数据(x,f(x)),x为一向量;或则是解析表达式非常复杂,不便于计算和使用.因此我们需要寻找一个计算比较简单的函数S(x)近似代替f(x),并使得S(x)=f(x),这种方法就称为插值法. 常用的插值法有: 一维插值法:拉格朗日插值.牛顿插值.分段低次插值.埃尔米特插值.样条插值. 二维插值法:双线性插值.双二次插值. 拉格朗日插值法 已知函数f(x

【BZOJ】2655: calc 动态规划+拉格朗日插值

[题意]一个序列$a_1,...,a_n$合法当且仅当它们都是[1,A]中的数字且互不相同,一个序列的价值定义为数字的乘积,求所有序列的价值和.n<=500,A<=10^9,n+1<A<mod<=10^9,mod是素数. [算法]动态规划+拉格朗日插值 [题解]这道题每个数字的贡献和序列选了的数字积关系密切,所以不能从序列角度考虑(和具体数字关系不大). 设$f_{n,m}$表示前n个数字(值域)中取m个数字的答案,那么枚举取或不取数字n,取n时乘n且有j个位置可以插入,即:

拉格朗日插值Python代码实现

1. 数学原理 对某个多项式函数有已知的k+1个点,假设任意两个不同的都互不相同,那么应用拉格朗日插值公式所得到的拉格朗日插值多项式为: 其中每个lj(x)为拉格朗日基本多项式(或称插值基函数),其表达式为: 2. 轻量级实现 利用 直接编写程序,可以直接插值,并且得到对应的函数值.但是不能得到系数,也不能对其进行各项运算. def h(x,y,a): ans=0.0 for i in range(len(y)): t=y[i] for j in range(len(y)): if i !=j:

Educational Codeforces Round 7 F - The Sum of the k-th Powers 拉格朗日插值

The Sum of the k-th Powers There are well-known formulas: , , . Also mathematicians found similar formulas for higher degrees. Find the value of the sum modulo 109 + 7 (so you should find the remainder after dividing the answer by the value 109 + 7).

hdu 4715 素数打表

先利用筛法完成素数打表 再从小到大判断即可 #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> using namespace std; const int Max = 1e6 + 50; int n; int isPrime[Max]; int tblPrime[Max];

BZOJ4599[JLoi2016&amp;LNoi2016]成绩比较(dp+拉格朗日插值)

这个题我们首先可以dp,f[i][j]表示前i个科目恰好碾压了j个人的方案数,然后进行转移.我们先不考虑每个人的分数,先只关心和B的相对大小关系.我们设R[i]为第i科比B分数少的人数,则有f[i][j]=sum f[i-1][k]*C(k,j)*C(n-1-k,R[i]-j)  (k>=j) 怎么解释呢,首先前i-1科有k个人已经被碾压,k肯定大于等于j,然后考虑当前这一科有j个人被碾压,那么就需要从k个人中选出j个来即C(k,j),然后从剩下的有R[i]-j个人比B考的少,这些人必须是之前i

拉格朗日插值

一,介绍 学过FFT的人都应该知道什么叫做插值,插值的意思就是说将一个多项式从点值表达转变成系数表达. 在FFT的插值中为什么可以做到n log n,是因为单位复数根的关系. 那对于普通的插值应该怎么办呢?解方程是一种方法,但是这个在计算机中十分不现实. 所以有许多种插值的方法,其中比较普及的就是拉格朗日插值. 二,定义 对某个多项式函数,已知有给定的k + 1个取值点: 其中对应着自变量的位置,而对应着函数在这个位置的取值. 假设任意两个不同的xj都互不相同,那么应用拉格朗日插值公式所得到的拉

【BZOJ】4559: [JLoi2016]成绩比较 计数DP+排列组合+拉格朗日插值

[题意]n位同学(其中一位是B神),m门必修课,每门必修课的分数是[1,Ui].B神碾压了k位同学(所有课分数<=B神),且第x门课有rx-1位同学的分数高于B神,求满足条件的分数情况数.当有一位同学的一门必修课分数不同时视为两种情况不同.n,m<=100,Ui<=10^9. [算法]计数DP+排列组合+拉格朗日插值 [题解]把分数作为状态不现实,只能逐门课考虑. 设$f[i][j]$表示前i门课,有j个同学被碾压的情况数,则有: $$f[i][j]=g(i)\cdot\sum_{k=j