ZOJ 3857 Hypersphere
2013年长沙网络赛的一道题, 恩第一眼根本没看懂题意, 后来才知道这个题是给定K和L两个正整数之后求
⌊(L+L∗(L−1)−−−−−−−−−√)K⌋mod
K
的值前面一部分是向下取整
这个题首先 直接通过快速幂来求的, 抛开浮点数误差不谈, K的值可能很大, 直接计算前面想下去整部分的话double应该都存不下
而这个题神奇的地方就在这里, 通过巧妙的共轭的构造使得原本计算的浮点数变成了整数
构造数列
a
使得
那么
a[t]
的左半部分是我们需要的, 而对于右半部分, 可以发现对于L >= 1, 这是一个最大为1, 最小是0.5的数的t次方, 证明如下:
(L−L∗(L−1)−−−−−−−−−√)=L−−√(L−−√−L−1−−−−−√)=L√L√+L−1√=11+1−1L√
当L = 1时最大时1, L趋近于无穷大是其值为0.5, 所以右半部分一定是一个最大为1的数, 并且可以趋近至0
另外一个巧妙的地方就是, 这个数列a的每一项都是整数, 这个是数列中的一个结论, 首先二次方程的两个根是
x1=L+L(L−1)−−−−−−−√,x2=L−L(L−1)−−−−−−−√
可以发现
x1+x2=2L,x1x2=L
这是方程
x2−2Lx+L=0
的两个根于是通过
a[0]=2,a[1]=2∗L
, 且有方程
a[i+1]−2La[i]+La[i−1]=0
得到所有的数列a中的数(这个数列递推什么的是高中数学竞赛里的内容吧..)
因为数列a中的数都是整数, 可以在递推的同时进行取模操作, 另外由于最后只需要左边的项, 而右边那项的值不超过1, 只需要将最后得到的结果减去1就可以了, 相当于浮点数向下取整的操作
至于数列a的转移方程, 很明显是线性转移, 通过矩阵快速幂来加速计算即可, 例如构造矩阵转移:
[anan−1][2L−L10]=[an+1an]
代码如下:
/*
* Author: Gatevin
* Created Time: 2015/7/17 13:27:13
* File Name: ZOJ3857.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
lint K, L;
struct Matrix
{
lint a[4][4];
Matrix()
{
for(int i = 0; i < 2; i++)
for(int j = 0; j < 2; j++)
a[i][j] = i == j ? 1 : 0;
}
lint* operator [] (int i)
{
return a[i];
}
};
Matrix operator * (Matrix &m1, Matrix &m2)
{
Matrix ret;
for(int i = 0; i < 2; i++)
for(int j = 0; j < 2; j++)
{
ret[i][j] = 0;
for(int k = 0; k < 2; k++)
ret[i][j] = (ret[i][j] + m1[i][k]*m2[k][j]) % K;
}
return ret;
}
Matrix quick(Matrix base, int pow)
{
Matrix I;
while(pow)
{
if(pow & 1) I = I*base;
base = base*base;
pow >>= 1;
}
return I;
}
int main()
{
while(~scanf("%lld %lld", &K, &L))
{
Matrix A;
A[0][0] = 2*L % K;
A[0][1] = 1;
A[1][0] = (L % K == 0) ? 0 : K - (L % K);
A[1][1] = 0;
A = quick(A, K - 1);
lint ans = (2*L*A[0][0]) % K + (2*A[1][0]) % K;
ans = (ans - 1 + K) % K;
printf("%lld\n", ans);
}
return 0;
}