1011: [HNOI2008]遥远的行星
Time Limit: 10 Sec Memory Limit: 162 MBSec Special Judge
Submit: 2480 Solved: 895
Description
直线上N颗行星,X=i处有行星i,行星J受到行星I的作用力,当且仅当i<=AJ.此时J受到作用力的大小为 Fi->j=Mi*Mj/(j-i) 其中A为很小的常量,故直观上说每颗行星都只受到距离遥远的行星的作用。请计算每颗行星的受力,只要结果的相对误差不超过5%即可.
Input
第一行两个整数N和A. 1<=N<=10^5.0.01< a < =0.35
接下来N行输入N个行星的质量Mi,保证0<=Mi<=10^7
Output
N行,依次输出各行星的受力情况
Sample Input
5 0.3
3
5
6
2
4
Sample Output
0.000000
0.000000
0.000000
1.968750
2.976000
HINT
精确结果应该为0 0 0 2 3,但样例输出的结果误差不超过5%,也算对
Source
题意:题目意思明了,给一个数列M[1....n],每一位都有一个值,第 i 个值为 所有 (j <= i * A ),M[ i ]*M[ j ] / (i - j) 的和
分析:显然有一个O(n^2)的做法,即对每个点 i,都暴力枚举 1... floor( i * A )的点求和,
但注意到误差最多有5%,所以我们可以不求精确值,考虑放缩思想,、把1....floor( i * A)分解成多段,每段都可以简单的求和,并且误差在5%以内
显然,我们首先可以把M[i]提取公因数,第 i 个值为 M[i] * ( 所有M[j] / (i - j)的和 ( 1 <= j <= i * A) )
即 M[i]*( M[j]/(i-j) + M[j-1]/(i-j+1) + M[j-2]/(i-j+2) + .... + M[1]/(i-1) )
一个显然的放缩为 对于一段 j, j-1, j-2, ......,j-k,放大为(M[j] + M[j-1] + M[j-2] + ..... + M[j-k]) / (i - j)
为使误差小于5%,我们令
M[j-k]/(i - j) - M[j-k]/(i - j + k) <= M[j-k]/(i - j + k) * 5%
这样必定有误差小于5%
我们令x = i - j,约掉M[j+k],变形的
1/x - 1/(x+k) <= 1 / (x+k) * 5%
(x+k) - x <= x * 5%
k <= x/20
即这一段最长为 j ..... j-(i-j)/20
则下一段起点为 j-(i-j)/20-1,这样一段段分下去,一直到分完 i*A ..... 1
如果这样分组,最多有多少段?
我们去极限情况,n = 100000,A = 0.35
这样得到也不超过10段,时间复杂度完全可以接受
综上所述,本题得解
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <deque> 6 #include <vector> 7 #include <queue> 8 #include <iostream> 9 #include <algorithm> 10 #include <map> 11 #include <set> 12 #include <ctime> 13 using namespace std; 14 typedef long long LL; 15 typedef double DB; 16 #define For(i, s, t) for(int i = (s); i <= (t); i++) 17 #define Ford(i, s, t) for(int i = (s); i >= (t); i--) 18 #define MIT (2147483647) 19 #define INF (1000000001) 20 #define MLL (1000000000000000001LL) 21 #define sz(x) ((int) (x).size()) 22 #define clr(x, y) memset(x, y, sizeof(x)) 23 #define puf push_front 24 #define pub push_back 25 #define pof pop_front 26 #define pob pop_back 27 #define ft first 28 #define sd second 29 #define mk make_pair 30 inline void SetIO(string Name) { 31 string Input = Name+".in", 32 Output = Name+".out"; 33 freopen(Input.c_str(), "r", stdin), 34 freopen(Output.c_str(), "w", stdout); 35 } 36 37 const int N = 100010; 38 const DB Eps = 1e-6; 39 int n; 40 DB A, M[N]; 41 DB Sum[N], Ans[N]; 42 43 inline void Input() { 44 scanf("%d%lf", &n, &A); 45 For(i, 1, n) scanf("%lf", &M[i]); 46 } 47 48 inline void Solve() { 49 For(i, 1, n) Sum[i] = Sum[i-1]+M[i]; 50 51 For(i, 1, n) { 52 int S = (int) floor(1.0*i*A); 53 DB Cnt = 0.0; 54 while(S) { 55 int K = i-S; 56 int D = K/20; 57 int _K = min(K+D, i-1); 58 int _S = i-_K-1; 59 Cnt += (Sum[S]-Sum[_S])/((DB) K); 60 S = _S; 61 } 62 Ans[i] = Cnt*M[i]; 63 } 64 65 For(i, 1, n) printf("%.6lf\n", Ans[i]); 66 } 67 68 int main() { 69 SetIO("1011"); 70 Input(); 71 Solve(); 72 return 0; 73 }