sduacm16级寒假训练 自测

9 篇文章 0 订阅
4 篇文章 0 订阅

POJ 3579 Median

【sol】

显然,每个点与其他的点形成的差是有序的,这样相当于有n组升序的序列,询问中位数。二分中位数,然

后判断比他小的数的个数,复杂度n* logn*logn

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 100000 + 50;
int a[N], n, m;
bool cal(int x)
{
    int num = 0;
    for(int i = 0; i < n; i++)
        num += n - (lower_bound(a, a + n, a[i] + x) - a);
    return(num > m / 2);
}
int main()
{
    while(~scanf("%d", &n))
    {
        m = n * (n - 1) / 2;
        for(int i = 0; i < n; i++)
            scanf("%d", &a[i]);
        sort(a, a + n);
        int l = 0,  r = 1e9 + 5;
        int ans;
        while(l <= r)
        {
           int mid = (l + r) >> 1;
           if (cal(mid))
           {
               ans = mid;
              // cout<<"fu"<<ans<<endl;
               l = mid + 1;
           } else
           r = mid - 1;
        }
        printf("%d\n", ans);
    }
    return 0;
}

POJ 1742 Coins

【sol】

裸的多重背包,但是普通的多重背包会超时,但是如果一件物品的总体积如果超过背包总体积的话,完全可

以用完全背包搞,这样可以优化时间复杂度。

#include<cstdio>
#include<iostream>
#include<cstring>
const int N = 100000 + 50;
bool f[N];
int a[N], c[N], v[N], top, ans;
using namespace std;
int n, m;
void cal(int x, int num)
{
    int tot = 1;
    while(num >= tot)
    {
        v[++top] = tot * x;
        num -= tot;
        tot *= 2;
    }
    if (num)
        v[++top] = num * x;
}
int main()
{
    while(~scanf("%d%d", &n, &m) && n)
    {
        memset(f, 0, sizeof(f));
        top = 0;
        ans = 0;
        for(int i = 0; i < n; i++)
            scanf("%d", &a[i]);
        for(int i = 0; i< n; i++)
            scanf("%d", &c[i]);
        for(int i = 0; i < n; i++)
            if(a[i] * c[i] < m)
                cal(a[i], c[i]);
        f[0] = true;
        for(int i = 1; i <= top; i++)
            for(int j = m; j >= v[i]; j--)
               f[j] = f[j] || f[j - v[i]];
        for(int i = 0; i < n; i++)
            if(a[i] * c[i] >= m)
              for(int j = a[i]; j <= m; j++)
                f[j] = f[j] || f[j - a[i]];
        for(int j = 1; j <= m; j++)
            if (f[j])
              ans++;
        printf("%d\n", ans);
    }
    return 0;
}

Visible Lattice Points

没找到来源………..

【Pro】

给定整数n, 询问有多少对(i,j),(i,j < n),使得gcd(i,j) = 1,有多组数据

【sol】

完全可以先暴力出来所有答案(没错,就是打表,,,,),如果这道题的数据范围变大的话,其实可以用

欧拉函数来搞,即ans = ∑f(i),先用nlogn来搞出f(i)的值…

#include<cstdio>
#include<iostream>
#include<cstring>
const int N = 1000 + 50;
int f[N], Case = 0;
using namespace std;
int n, m, T;
int gcd(int a, int b)
{
    if(b == 0)
        return a;
    return gcd(b, a % b);
}
int main()
{
    f[1] = 3;
    for(int t = 2; t <= 1000; t++)
    {
        f[t] = f[t - 1];
        for(int i = 1; i < t; i++)
            if (gcd(i, t) == 1)
            f[t] += 2;
    }
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        printf("%d %d %d\n", ++Case, n, f[n]);
    }
    return 0;
}

POJ 1845 Sumdiv(数论,求A^B的所有约数和)

【sol】

先质因数分解一下,假设为2^a1 * 3^ a2* 5^a3然后因子和就是(2^0 + 2^ 1 + 2^ 2+…..+2^ a1)*(3^0 +

3^1 + …..+3^ a2) *(5^0 + …. + 5^a3),这样每项用等比数列公式来搞就可以了,但是公式有除法,就需要

逆元了

【吐槽】

wa掉了好多发,注意两个问题,第一,即公比为1的时候特殊处理,第二,当p为9901时,此时结果并不为

零…毕竟0^0 = 1

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
const int N = 10000 + 50;
const int P = 9901;
LL p[N], a[N], top = 0, n, m, inv[N];


void cal(LL n)
{
    for(int i = 2; i <= sqrt(n); i++)
        if (n % i == 0)
    {
        top++;
        p[top] = i;
        while (n % i == 0)
        {
            a[top]++;
            n /= i;
        }
    }
    if (n > 1)
    {
        p[++top] = n;
        a[top] = 1;
    }
}
LL quick(LL a, LL b)
{
    LL ans = 1;
    a = a % P;
    while(b)
    {
        if (b % 2 == 1)
            ans = ans * a % P;
        b /= 2;
        a = a * a % P;
    }
    return ans;
}
int main()
{
    inv[1] = 1;
    for(int i = 2; i < P; i++)
    {
        inv[i] = (P - P / i) * inv[P % i];
        inv[i] %= P;
    }
    while(cin>>n>>m){
    if (n == 0)
    {
        printf("0");
        continue;
    }
    top = 0;
    memset(a, 0, sizeof(a));
    cal(n);
    LL ans = 1;
    for(int i = 1; i <= top; i++)
    {
        p[i] %= P;
        if (p[i] == 0)
        {
           // printf("0");
           // ans = 1;
            continue;
        }
        if(p[i] == 1)
        {
            LL tot = a[i] * m + 1;
            tot %= P;
            ans = ans * tot % P;
            continue;
        }
       // cout<<"test"<<p[i]<<"ko"<<a[i] * m + 1<<endl;
        LL tot = quick(p[i], a[i] * m + 1);
      //  cout<<"tot"<<tot<<endl;
        tot = (tot - 1 + P);
        tot = (tot % P )* inv[p[i] - 1];
       // cout<<"tot"<<tot<<endl;
        ans = ans * tot % P;
    }
    cout<<ans;
    }
    return 0;
}

POJ1837 Balance

【sol】

此问题等价于一组物品中只能选取一件物品,询问某价值的方案数,即把一系列的挂钩的权重与砝码的乘积

组成一组物品,这里的权重会有负数,最后的答案就是f[0]

#include<cstdio>
#include<iostream>
using namespace std;
const int N = 5000 * 2;
int f[20 + 5][N];
int w[20 + 5], a[20 + 5];
int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 0; i < n; i++)
        scanf("%d", &a[i]);
    for(int i = 0; i < m; i++)
        scanf("%d", &w[i]);
    f[0][5000] = 1;
    for(int i = 0; i < m; i++)
    {
        for(int j = 0; j <= 10000; j++)
            if (f[i][j] != 0)
            for(int k = 0; k < n; k++)
            {
                int t = j + a[k] * w[i];
                 f[i + 1][t] += f[i][j];
              //  printf("f[%d,%d]=%d\n", i + 1, t, f[i + 1][t]);
            }
    }
    printf("%d", f[m][5000]);
    return 0;
}

POJ 3267 The Cow Lexicon

【sol】

暴力,显然不现实,用dp来搞,f[i]代表前i个字符的最大匹配长度,这样f[i] = f[j] + new,这样考虑每一步

的决策,即判断一个字符串是否是另外一个字符串的子串,可以用O(n)来搞,但是对于每个区间,都需要与

每个单词匹配一下,这样如果m个单词,长度为n的字符串,复杂度为n* n* m*m,我们需要优化,如果字

符串的(l,r)可以匹配某单词的话,(l,r + 1),(l,r + 2)都可以了,所以预处理的时候枚举起点,做一次匹配,这

样对于不同的终点不需要再次匹配了

#include<cstdio>
#include<iostream>
using namespace std;
string a[600 + 5];
string ss;
const int N = 300 + 5;
int n, m;
int tot[N][N], f[N];
int cal(int t, string s2)
{
    int l = 0;
    int k = ss.length();
    for(int i = t; i < ss.length(); i++)
        {
            if (ss[i] == s2[l])
               l++;
            if (l == s2.length())
            {
                k = i;
                break;
            }
        }
    return k;
}
int main()
{
    scanf("%d%d", &m, &n);
    cin>>ss;
    for(int i = 0; i < m; i++)
        cin>>a[i];
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < m; j++)
        {
            int t = cal(i, a[j]);
          //  if (t != ss.length())
          //  printf("%d ~ %d\n", i, t);
            int ans = a[j].length();
            for(int k = t; k < ss.length(); k++)
                tot[i][k] = max(tot[i][k], ans);
        }
    }
    for(int i = 1; i <= n; i++)
      for(int j = 0; j < i; j++)
        f[i] = max(f[i], f[j] + tot[j][i - 1]);
    printf("%d", n - f[n]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值