[hdu5312]数的拆分,数学推导

题意:给定一个序列,a[n]=3n(n-1)+1,n>=1,求给定的m(m<=1e9)最少可以用几个a里面的数表示(可以重复)

 思路:对答案分类

(1)假定答案为1,则m必定是a中的某一个数,直接查找即可,复杂度O(logn)

(2)假定答案为2,则m必定可以拆分成两个a中的数之和,用两指针分别从头和尾向中间扫,判断是否可以构成m,复杂度O(n)

(3)假定答案大于等于3,设答案为k,即k>=3,则必有m=a[i1]+a[i2]+...+a[ik],由于a[i]=3i(i-1)+1=6[i(i-1)/2]+1,所以有:

 m=6[i 1 (i 1 -1)/2+i 2 (i 2 -1)/2+...+i k (i k -1)/2]+k                        

所以(m-k)%6==0恒成立,也就是说如果得出了答案k,那么答案一定满足(m-k)%6==0,这是必要性;当k>=3时,令b=(m-k)/6,因为任意一个自然数最多只需要3个三角形数即可表示,所以b=i1(i1-1)/2+i2(i2-1)/2+...+ik(ik-1)/2恒有解,这是充分性。故答案k需满足k>=3且(m-k)%6==0,由于是求最小个数,k从3枚举到第一次满足(m-k)%6==0即可得到答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <ctime>
#include <deque>
#include <queue>
#include <algorithm>
using  namespace  std;
 
#define pb push_back
#define mp make_pair
#define X first
#define Y second
#define all(a) (a).begin(), (a).end()
 
void  readInt(){} void  RI( int &X){ scanf ( "%d" ,&X);} template < typename ...R>
void  RI( int &f,R&...r){RI(f);RI(r...);} void  RIA( int *p, int *q){ int  d=p<q?1:-1;
while (p!=q){ scanf ( "%d" ,p);p+=d;}} void  print(){cout<<endl;} template < typename  T>
void  print( const  T t){cout<<t<<endl;} template < typename  F, typename ...R>
void  print( const  F f, const  R...r){cout<<f<< ", " ;print(r...);} template < typename  T>
void  print(T*p, T*q){ int  d=p<q?1:-1; while (p!=q){cout<<*p<< ", " ;p+=d;}cout<<endl;}
 
typedef  pair< int int > pii;
typedef  long  long  ll;
typedef  unsigned  long  long  ull;
 
template < typename  T> bool  umax(T &a,  const  T &b) {
     return  a >= b?  false  : (a = b,  true );
}
/* -------------------------------------------- */
 
vector< int > table;
 
void  init() {
     for  ( int  i = 1; ; i ++) {
         ll buf = 3ll * i * (i - 1) + 1;
         if  (buf > 1e9 + 7)  break ;
         table.pb(( int )buf);
     }
}
 
bool  chk( int  x) {
     int  l = 0, r = upper_bound(all(table), x - table[0]) - table.begin() - 1;
     while  (l < r && table[l] + table[r] != x) {
         l ++;
         while  (l < r && table[l] + table[r] > x) r --;
     }
     return  table[l] + table[r] == x;
}
 
int  main() {
#ifndef ONLINE_JUDGE
     freopen ( "in.txt" "r" , stdin);
#endif // ONLINE_JUDGE
     int  T;
     cin >> T;
     init();
     while  (T --) {
         int  x;
         RI(x);
         if  (find(all(table), x) != table.end()) {
             puts ( "1" );
             continue ;
         }
         if  (chk(x)) {
             puts ( "2" );
             continue ;
         }
         for  ( int  k = 3; ; k ++) {
             if  ((x - k) % 6 == 0) {
                 printf ( "%d\n" , k);
                 break ;
             }
         }
     }
     return  0;
}

 

转载于:https://www.cnblogs.com/jklongint/p/4681712.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值