HDU - 6253 Knightmare (打表+拉格朗日插值)
Posted asdfsag
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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了~
以上是关于HDU - 6253 Knightmare (打表+拉格朗日插值)的主要内容,如果未能解决你的问题,请参考以下文章