ACM – Uva10717 – lcm+dfs

今天不适合刷题。。。

题意:一个桌子4条腿,每条腿由一种硬币构成,四条腿必须一样长,请问如何最接近给出的标准桌子高度,输出两个最接近的值。

更新两个值的时候错误的判断如果lcm大于h就不再更新了,却没有想到高于h的部分依然可以更新,只是不能更新小于h的部分了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
#include <cmath>
#include <bitset>
using namespace std;
// 大数,内存处理
#define INF 0x3f3f3f3f
#define ll long long int
#define MEM(a) memset(a, 0, sizeof(a))
#define MEMM(a) memset(a, -1, sizeof(a))
#define DEB(x, n) cout << (x) << " " << (n) << endl
#define FOR(i, s, e) for(int (i) = (s); (i) < (e); (i)++)
const double PI = acos(-1.0);
#define CR printf("\n")
// 调试用
    template <class Type>
void debug(Type a[], int len)
{
    for(int i = 0; i < len ; i++)
    {
        cout << a[i] << " ";
    }
    cout << endl;
}
int gcd(int a, int b)
{
    return b == 0? a: gcd(b, a % b);
}
const int maxn = 50 + 10;
int a[maxn];
int n, t;
int table[15];
int ans1[15];
int ans2[15];
void output()
{
    for(int i = 0; i < t; i++)
        printf("%d %d\n", ans1[i], ans2[i]);
}
// 寻找边界
bool findmax(int lcm, int num, int &mint, int &maxt)
{
    // if(lcm > num)
    // return false;
    int t = num / lcm * lcm;
    if(t == num)
    {
        mint = num;
        maxt = num;
    }
    else
    {
        mint = t;
        maxt = t + lcm;
    }
    // return 1;
}
// 检查
void check(int lcm)
{
    int mint, maxt;
    for(int i = 0; i < t; i++)
    {
        findmax(lcm, table[i], mint, maxt);
        if(ans1[i] == -1 || mint > ans1[i]) ans1[i] = mint;
        if(ans2[i] == -1 || maxt < ans2[i]) ans2[i] = maxt;
    }
}
// 枚举四个腿
void fs(int c, int num, int lcm)
{
    if(num == 4)
    {
        check(lcm);
        return;
    }
    for(int i = c; i < n; i++)
    {
        int temp = lcm;
        lcm = lcm / gcd(lcm, a[i]) * a[i]; // 可能溢出
        fs(i+1, num+1, lcm);
        lcm = temp;
    }
}
int main()
{
#ifdef DEBUG
    freopen("input", "r", stdin);       //从input文件中读入
    // freopen("output", "w", stdout);     //输出到output文件
#endif
    while(~scanf("%d%d", &n, &t) && n != 0)
    {
        for(int i = 0; i< n; i++) scanf("%d", &a[i]);
        for(int i = 0; i < t; i++)
        {
            scanf("%d", &table[i]);
            ans1[i] = -1;
            ans2[i] = -1;
        }
        fs(0, 0, 1);
        output();
    }
    return 0;
}

ACM – UVa10791 – divide prime

谢天谢地终于过了。
就是求分解质因数后的和,如果是质数那么返回1+n本身。

  1. 一开始直接暴力求两个质因数的情况,铁定不对啊。。
  2. 第二次发现策略有问题,转为使用枚举质因数,然后发现仅仅是质因数LCM有问题啊。。
  3. 第三次没有考虑质数
  4. 第四次没有考虑Case
  5. 第五次不记得了。
  6. 第七次AC,哭了真是。。一定要先分析好题目啊。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
#include <cmath>
#include <bitset>
using namespace std;
// 大数,内存处理
#define inf 0x3f3f3f3f
#define ll long long int
#define mem(a) memset(a, 0, sizeof(a))
#define memm(a) memset(b, -1, sizeof(b))
#define deb(x, n) cout << (x) << " " << (n) << endl
#define cr printf("\n")
// 调试用
    template <class type>
void debug(type a[], int len)
{
    for(int i = 0; i < len ; i++)
    {
        cout << a[i] << " ";
    }
    cout << endl;
}
ll solve(int n)
{
    if(n == 1) return 2;
    ll ans = 0;
    ll sq = sqrt(n);
    int cnt, pf = 0;
    for(ll i = 2; i <= sq;i++)
    {
        if(n % i == 0)
        {
            cnt = 1;
            while(n % i == 0)
            {
                cnt *= i;
                n /= i;
            }
            pf ++;
            ans += cnt;
        }
    }
    if(1 != n)
    {
        ans += n;
        pf++;
    }
    // n为质数
    if(pf <= 1) ans ++;
    return ans;
}
int main()
{
#ifdef debug
    // freopen("input", "r", stdin);
#endif
    int n;
    int c = 1;
    // const int maxn = pow(2, 31) -1;
    // printf("%d\n", maxn);
    ll ans;
    while(scanf("%d", &n) && n)
    {
        ans = solve(n);
        printf("Case %d: ", c++);
        printf("%lld\n", ans);
    }
    return 0;
}

ACM一个队伍的建成之我见

今天刷题数论题目的时候有所感想。

  1. 首先建立一支比较强悍的ACM队伍第一点就是保证刷题量,看再多的书,不做练习肯定是不行的。
  2. 选一本正确的指导教材,比如说李汝佳的白皮书,简单易懂,快捷粗暴,按照给出的分类进行训练,效果更佳。
  3. 正确的分类。ACM/ICPC的题目各个方向等等都有,各个都很精通对于大学才刚开始培养的acmer恐怕不是很现实,不如巩固共同部分,着重培养较为偏门的部分。最简单的方法也是通过分类,给学生一个兴趣方向,数论的可以顺带研究密码学等学科,对于以后的长远发展也是有很大的好处 — 反而,样样精通可能造成的结果就是样样不精通。当然,如果敏而好学,那么不局限于这一点。

想想去年这个时间在刷一份简单的模板,感觉用处实在是不大,问题都没有好好的理解。

ACM – UVA11121 – 进制

题意是由10进制转换成-2进制,但是明显我分析错了,所以写了调了接近一下午的bug。。。

有时间再写正常的题解。
题解:


如果当前数位和输入n符号相同,那么不做处理,如果不同且为1,那么下一位(即左边一位)做+1处理 — 因为下一位是上一位的2倍,就相当于做一个变号处理。

ACM – Uva10673 – 扩展gcd

水题一发。。直接使用floor和ceil函数,然后暴力即可。。。

如果使用快速的方法,就是扩展gcd。其中d的初始值没有关系,最后返回的是gcd(a,b).

暴力算法

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
#include <cmath>
#include <bitset>
using namespace std;
// 大数,内存处理
#define INF 0x3f3f3f3f
#define ll long long int
#define MEM(a) memset(a, 0, sizeof(a))
#define MEMM(a) memset(b, -1, sizeof(b))
#define DEB(x, n) cout << (x) << " " << (n) << endl
#define FOR(i, s, e) for(int (i) = (s); (i) < (e); (i)++)
const double PI = acos(-1.0);
#define CR printf("\n")
// 调试用
    template <class Type>
void debug(Type a[], int len)
{
    for(int i = 0; i < len ; i++)
    {
        cout << a[i] << " ";
    }
    cout << endl;
}
// ax + by = d
// 扩展gcd
void gcd(ll a, ll b, ll &d, ll &x, ll &y)
{
    if(!b) { d = a; x = 1; y = 0; }
    else { gcd(b, a%b, d, y, x);  y-= x*(a/b);  }
}
int main()
{
#ifdef DEBUG
    // freopen("input", "r", stdin);       //从input文件中读入
    // freopen("output", "w", stdout);     //输出到output文件
#endif
    int T;
    cin >> T;
    ll x, k;
    while(T--)
    {
        scanf("%lld%lld", &x, &k);
        double c = x/(double)k;
        ll fl = floor(c);
        ll ce = ceil(c);
        // printf("%lld %lld\n", a, b);
        bool find = 0;
        for(ll i = 0; !find; i++)
            for(ll j = 0; i * fl + j *ce <= x; j++)
            {
                if(i*fl + j *ce == x)
                {
                    printf("%lld %lld\n", i, j);
                    find =1;
                    break;
                }
            }
    }
    return 0;
}

扩展gcd算法

速度更快,0.012

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
#include <cmath>
#include <bitset>
using namespace std;
// 大数,内存处理
#define INF 0x3f3f3f3f
#define ll long long int
#define MEM(a) memset(a, 0, sizeof(a))
#define MEMM(a) memset(b, -1, sizeof(b))
#define DEB(x, n) cout << (x) << " " << (n) << endl
#define FOR(i, s, e) for(int (i) = (s); (i) < (e); (i)++)
const double PI = acos(-1.0);
#define CR printf("\n")
// 调试用
    template <class Type>
void debug(Type a[], int len)
{
    for(int i = 0; i < len ; i++)
    {
        cout << a[i] << " ";
    }
    cout << endl;
}
// ax + by = d
// 扩展gcd
void gcd(ll a, ll b, ll &d, ll &x, ll &y)
{
    if(!b) { d = a; x = 1; y = 0; }
    else { gcd(b, a%b, d, y, x);  y-= x*(a/b);  }
}
int main()
{
#ifdef DEBUG
    // freopen("input", "r", stdin);       //从input文件中读入
    // freopen("output", "w", stdout);     //输出到output文件
#endif
    int T;
    cin >> T;
    ll x, k;
    while(T--)
    {
        scanf("%lld%lld", &x, &k);
        double c = x/(double)k;
        ll fl = floor(c);
        ll ce = ceil(c);
        ll a1, a2, g;
        gcd(fl, ce, g, a1, a2);
        a1 *= x/g;
        a2 *= x/g;
        printf("%lld %lld\n", a1, a2);
    }
    return 0;
}

ACM – Uva106 – 勾股定理

從維基百科的勾股數條目參考來的通解:
給一個任意數對(X,Y),用以下公式代替

$A = X^2 – Y^2$
$B = 2XY$
$C = X^2 + Y^2$
得出的A,B,C就是一組勾股數。

若 (X,Y) 恰好互質而且一奇一偶,那麼會得到一組(A,B,C)互質的勾股數。
知道通解後,雙層迴圈跑 (X,Y) 就能找出所有互質勾股數。

按照台湾同胞的题解。 — 勾股数公式也是很神奇的啊。。。

此外还学习了bitset,因为是按位存储,所以比一般的bool来的更加迅速,也更加节省时间。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
#include <cmath>
#include <bitset>
using namespace std;
// 大数,内存处理
#define INF 0x3f3f3f3f
#define ll long long int
#define MEM(a) memset(a, 0, sizeof(a))
#define MEMM(a) memset(b, -1, sizeof(b))
#define DEB(x, n) cout << (x) << " " << (n) << endl
const double PI = acos(-1.0);
#define CR printf("\n")
// 调试用
    template <class Type>
void debug(Type a[], int len)
{
    for(int i = 0; i < len ; i++)
    {
        cout << a[i] << " ";
    }
    cout << endl;
}
int gcd(int a, int b) { return a % b == 0 ? b : gcd(b, a%b); }
int odd(int a, int b) {
    return (a%2 && !(b%2)) || (b%2 &&!(a%2));
}
int judge(int a, int b)
{
    return (gcd(a, b) == 1 && odd(a, b));
}
int n;
const double eps = 1e-15;
const int N = 1e6+10;
bitset <N> p;
int main()
{
#ifdef DEBUG
    // freopen("input", "r", stdin);       //从input文件中读入
    // freopen("output", "w", stdout);     //输出到output文件
#endif
    while(~scanf("%d", &n))
    {
        p.set();
        int cnt = 0;
        for(int x = 1; x < 1000; x ++)
            for(int y = x+1; ; y += 2)
            {
                int a, b, c;
                if(judge(x, y))
                {
                    a = y*y - x*x;
                    b = 2*x*y;
                    c = y*y + x*x;
                    if(c > n) break;
                    cnt ++;
                    int ta = a, tb = b, tc = c;
                    while(tc <= n)
                    {
                        p[ta] = p[tb] = p[tc] = 0;
                        ta += a, tb += b, tc += c;
                    }
                }
            }
        int c2  = 0;
        for(int i = 1;i <= n; i++)
            if(p[i]) c2++;
        printf("%d %d\n", cnt, c2);
    }
    return 0;
}

ACM – 莫比乌斯反演

昨天卡了这道题目,今天特意来看看莫比乌斯反演。

title

其中,$∑_{d|n}$含义为整除.


ACdreamer大大的讲解


看了这篇文章即可= =

wikipedia的公式:

title

title

以上就是具体的公式,此外,还有线性筛法:

void Init()
{
    memset(vis,0,sizeof(vis));
    mu[1] = 1;
    cnt = 0;
    for(int i=2; i<N; i++)
    {
        if(!vis[i])
        {
            prime[cnt++] = i;
            mu[i] = -1;
        }
        for(int j=0; j<cnt&&i*prime[j]<N; j++)
        {
            vis[i*prime[j]] = 1;
            if(i%prime[j]) mu[i*prime[j]] = -mu[i];
            else
            {
                mu[i*prime[j]] = 0;
                break;
            }
        }
    }
}

这段代码也是摘自AC大大。我们得到这个东西有什么用?
简单来说,就是简化计算数论函数和的计算的过程,进行加速。

在HDU5212中,给出的题解是:

这道题需要一些莫比乌斯反演、线性筛的知识
定义$f(x)=x∗(x−1)$
题目所求即为$\Sigma(f(gcd(ai,aj)|i!=j,1≤i,j≤n)$
先用线性筛求出miu在[1,10000]的函数值
利用莫比乌斯反演公式我们可以$O(vlogv)$暴力求解出函数g(就是$f(n)/miu$)在[1,10000]的函数值,其中g满足:
$\Sigma(g(d)|x)$
这样所求答案即为:
$\Sigma(g(d)∗cnt(d)∗cnt(d)|1≤d≤10000)$,其中cnt函数满足:
cnt(x)=在a1,a2,..,an中是x的倍数的个数
而cnt的取值也可以$O(vlogv)$暴力计算出
所以总的时间复杂度就是$O(vlogv)$的

就是这样。

Bestcoder 2 题目

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
#include <cmath>
#include <bitset>
using namespace std;
// 大数,内存处理
#define INF 0x3f3f3f3f
#define ll long long int
#define MEM(a) memset(a, 0, sizeof(a))
#define MEMM(a) memset(b, -1, sizeof(b))
#define DEB(x, n) cout << (x) << " " << (n) << endl
const double PI = acos(-1.0);
#define CR printf("\n")
// 调试用
    template <class Type>
void debug(Type a[], int len)
{
    for(int i = 0; i < len ; i++)
    {
        cout << a[i] << " ";
    }
    cout << endl;
}
int main()
{
#ifdef DEBUG
    // freopen("input", "r", stdin);       //从input文件中读入
    // freopen("output", "w", stdout);     //输出到output文件
#endif
    int n;
    int record[10010];
    int a[10010];
    while(scanf("%d", &n) != EOF)
    {
        memset(record, 0, sizeof(record));
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        for(int i = 1; i <= n;i ++)
            for(int j = i+1; j <= n; j++)
            {
                if(a[j] % a[i] == 0)
                {
                    record[i] = j;
                    break;
                }
            }
        // debug(record, n+1);
        ll res = 0;
        for(int i = 1; i <= n; i++)
            res += record[i];
        printf("%lld\n", res);
    }
    return 0;
}

ACM – UVa10006 – 快速幂

水题一发。无奈自己没有好好审清题意,另外快速幂居然写错了- =

快速幂在过程中修改a的值,但是我却计算成了在偶数时相乘,调试半天。看来还是咩有好好理解啊。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
#include <cmath>
#include <bitset>
using namespace std;
// 大数,内存处理
#define INF 0x3f3f3f3f
#define ll long long int
#define MEM(a) memset(a, 0, sizeof(a))
#define MEMM(a) memset(b, -1, sizeof(b))
#define DEB(x, n) cout << (x) << " " << (n) << endl
const double PI = acos(-1.0);
#define CR printf("\n")
// 调试用
    template <class Type>
void debug(Type a[], int len)
{
    for(int i = 0; i < len ; i++)
    {
        cout << a[i] << " ";
    }
    cout << endl;
}
ll p;
ll pow_mod(ll a, ll n)
{
    ll ans = 1;
    a %= p;
    while(n)
    {
        if(n & 1) ans = ans * a % p;
        a = a * a % p;
        n >>= 1;
    }
    return ans;
}
// 3.
const int maxn = 10000000 + 10;
const int maxp = 700000;
int vis[maxn];
int prime[maxp];
// 筛素数
void sieve(int n)
{
    int m = (int) sqrt(n+0.5);
    memset(vis, 0, sizeof(vis));
    for(int i = 2; i <= m;i ++) if(!vis[i])
        for(int j = i*i; j <= n; j+=i) vis[j] = 1;
}
// 生成素数表
int gen_primes(int n)
{
    sieve(n);
    int c = 0;
    for(int i = 2; i <= n; i++) if(!vis[i])
        prime[c++] = i;
    return c;
}
int main()
{
#ifdef DEBUG
    // freopen("input", "r", stdin);       //从input文件中读入
    // freopen("output", "w", stdout);     //输出到output文件
#endif
    ll n;
    const int bound = 65000+10;
    gen_primes(bound);
    while(scanf("%lld", &n) && n!= 0)
    {
        // p is mod
        p = n;
        if(!vis[n])
            printf("%lld is normal.\n", n);
        else
        {
            bool find = 1;
            for(ll i = 2; i < n;i ++)
                if(pow_mod(i, n) != i)
                {
                    find = 0;
                    break;
                }
            if(find)
                printf("The number %lld is a Carmichael number.\n", n);
            else printf("%lld is normal.\n", n);
        }
    }
    return 0;
}

ACM – UVa138 – 数学基础

这道题目其实就是推个公式,主要还是考验编程技巧。。
首先double的精度范围是15-16位,浮点数运算还是最快的,依据最后一个答案(总共10组),平方为16位刚好够用。

如果想要最精确的结果,当然还是使用long double或者long long来来保证精度问题。不过计算速度就会有所损失,采取二分的方法加速比较好。


利用double的代码:

    const double eps = 1e-10;
    for(double i = 8; i < maxn;i ++)
    {
        double res = i*(i+1)/2;
        ll ans = sqrt(res);
        if(res - ans*ans < eps)
            printf("%10lld %10lf\n", ans, i);
    }
    printf("done");

利用long long的代码:

    for(ull i = 8; i < maxn;i ++)
    {
        ull res = i*(i+1)/2;
        ull l = i/2, r = i;
        ull mid, ans;
        bool find = 0;
        // bsearch
        while(l <= r)
        {
            mid = (l+r)/2;
            ans = mid * mid;
            if(ans == res)
            {
                find = 1;
                break;
            }
            else if(ans > res)
                r = mid - 1;
            else
                l = mid + 1;
        }
        if(find)
            printf("%10lld %10lld\n", mid, i);
    }
    printf("done");

不过有一点就是无论怎么个快法,不使用打表肯定是会超时的,所以算出来直接上交即可。= =。这在大赛中应该也是允许的吧。