2020年9月21日(完)

51Nod 1383 整数分解为2的幂
51Nod 3212 数字变位
51Nod 2128 前缀异或
LightOJ 1341 Aladdin and the Flying Carpet
POJ 3061 Subsequence
Codeforces 1263A Sweet Problem

51Nod 1383 整数分解为2的幂

题目链接:点击这里

1 = 1


2 = 1 + 1
2 = 2

3 = 1 + 1 + 1
3 = 1 + 2


4 = 1 + 1 + 1 + 1
4 = 1 + 1 + 2
4 = 2 + 2
4 = 4

5 = 1 + 1 + 1 + 1 + 1
5 = 1 + 1 + 1 + 2
5 = 1 + 2 + 2
5 = 1 + 4


6 = 1 + 1 + 1 + 1 + 1 + 1
6 = 1 + 1 + 1 + 1 + 2
6 = 1 + 1 + 2 + 2
6 = 2 + 2 + 2
6 = 1 + 1 + 4
6 = 2 + 4

7 = 1 + 1 + 1 + 1 + 1 + 1 + 1
7 = 1 + 1 + 1 + 1 + 1 + 2
7 = 1 + 1 + 1 + 2 + 2
7 = 1 + 2 + 2 + 2
7 = 1 + 1 + 1 + 4
7 = 1 + 2 + 4


8 = 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1
8 = 1 + 1 + 1 + 1 + 1 + 1 + 2
8 = 1 + 1 + 1 + 1 + 2 + 2
8 = 1 + 1 + 2 + 2 + 2
8 = 2 + 2 + 2 + 2
8 = 1 + 1 + 1 + 1 + 4
8 = 1 + 1 + 2 + 4
8 = 2 + 2 + 4
8 = 4 + 4
8 = 8

9 = ......


不难发现,当 为奇数时, 的划分数就等于 的划分数,即 所划分的每个等式只是在 所划分的每个等式的基础上多加了个 而已。

当 为偶数时,规律比较难找,我做题时也就卡在这了~

结论:当 为偶数时, 的划分数等于 的划分数加上 的划分数。

解释:以上面 的所有等式为例,

8 = 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1
8 = 1 + 1 + 1 + 1 + 1 + 1 + 2
8 = 1 + 1 + 1 + 1 + 2 + 2
8 = 1 + 1 + 2 + 2 + 2
8 = 1 + 1 + 1 + 1 + 4
8 = 1 + 1 + 2 + 4

上面这部分等于 的划分数,除去此部分,剩下的等式都是由 的幂组成,于是全都除以 ,可以发现,剩下的部分就等于 的划分数。

8 = 2 + 2 + 2 + 2
8 = 2 + 2 + 4
8 = 4 + 4
8 = 8

4 = 1 + 1 + 1 + 1
4 = 1 + 1 + 2
4 = 2 + 2
4 = 4

AC代码:

#include
#include
#include

using namespace std;

const int mod = 1000000007;
const int N = 1e6 + 10;

int f[N];

int main()
{
    int n;
    scanf("%d", &n);
    
    f[1] = 1;
    for(int i = 2; i <= n; i++)
    {
        if(i & 1)   f[i] = f[i - 1];
        else    f[i] = (f[i - 1] + f[i >> 1]) % mod;
    }
    
    printf("%d", f[n]);
    
    return 0;
}

51Nod 3212 数字变位

题目链接:点击这里

#include
#include
#include

using namespace std;

const int N = 110;

int a[N];

bool cmp(int a, int b)
{
    return a > b;
}

int main()
{
    int x;
    scanf("%d", &x);
    
    int cnt = 0;
    while(x)
    {
        a[cnt++] = x % 10;
        x /= 10;
    }
    
    sort(a, a + cnt, cmp);          // 从大到小 
    int maxx = 0;
    for(int i = 0; i < cnt; i++)    maxx = maxx * 10 + a[i];
    printf("%d ", maxx);
    
    sort(a, a + cnt);               // 从小到大 
    int i;
    for(i = 0; i < cnt; i++)
        if(a[i] != 0)
            break;
    
    if(i == cnt)    printf("0\n");
    else
    {
        printf("%d", a[i]);
        for(int j = 0; j < i; j++)  printf("0");
        for(int j = i + 1; j < cnt; j++)    printf("%d", a[j]);
    }
    
    return 0;
}

51Nod 2128 前缀异或

题目链接:点击这里

#include
#include
#include

using namespace std;

const int N = 100010;

int a[N];

int main()
{
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        a[i] = a[i - 1] ^ a[i];
    }
    
    int m, l, r;
    scanf("%d", &m);
    while(m--)
    {
        scanf("%d%d", &l, &r);
        printf("%d\n", a[r] ^ a[l - 1]);
    }
    
    return 0;
}

LightOJ 1341 Aladdin and the Flying Carpet

题目链接:点击这里

题意:现在已知地毯的面积和地毯可能的最小边的长度,您的任务是找出可能有多少种地毯。例如,地毯的面积为12,地毯的最小可能边是2,那么就可以有两种地毯,它们的边是 {2,6} 和 {3,4}。

思路:首先求出 的正约数的个数,然后再除以 得到 的正约数的对数,最后再枚举出 中 的正约数的个数,相减便是答案。

注意:如果每组样例都用 的时间求解出 的正约数个数,会超时。所以,先预处理出 以内的所有素数,然后在每次求解正约数个数时,直接利用已经筛好的素数即可。

AC代码:

#include
#include
#include

using namespace std;
typedef long long ll;
const int N = 1500000;

int p[N + 10], cnt;         // p[]存储所有素数
bool st[N + 10];            // st[x]存储x是否被筛掉

void get_primes()           // 线性筛 
{
    for(int i = 2; i <= N; i++)
    {
        if(!st[i])  p[cnt++] = i;
        for(int j = 0; p[j] <= N / i; j++)
        {
            st[p[j] * i] = true;
            if(i % p[j] == 0)   break;
        }
    }
}

int get_divisors(ll n)      // 求 n 的约数个数 
{
    int res = 1;
    for(int i = 0; p[i] <= n / p[i]; i++)
    {
        int c = 0;
        while(n % p[i] == 0)
        {
            c++;
            n /= p[i];
        }
        res *= c + 1;
    }
    if(n > 1)   res *= 1 + 1;
    return res;
}

int main()
{
    get_primes();
    
    int T;
    scanf("%d", &T);
    for(int k = 1; k <= T; k++)
    {
        ll a, b;
        scanf("%lld%lld", &a, &b);
        
        if(b * b >= a)
        {
            printf("Case %d: 0\n", k);
            continue;
        }
        
        int ans = get_divisors(a) / 2;
        
        for(int i = 1; i < b; i++)
            if(a % i == 0)
                ans--;
        
        printf("Case %d: %d\n", k, ans);
    }
    
    return 0;
}

POJ 3061 Subsequence

题目链接:点击这里

题意:给定长度为 的数列整数 以及整数 。求出总和不小于 的连续子序列的长度的最小值。如果解不存在,则输出 。

前缀和+二分

由于所有的元素都大于零,如果子序列 满足 ,那么,对于任何的 一定有 。

因此,首先预处理出前缀和 ,然后枚举起点 ,对于每一个起点 ,在其后面二分找到第一个满足 的终点 ,统计出所有区间长度 的最小值即可。

时间复杂度是

#include
#include
#include
#include

using namespace std;

const int N = 100010;
int s[N];

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int n, target, x;
        scanf("%d%d", &n, &target);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &x);
            s[i] = s[i - 1] + x;
        }
        
        if(s[n] < target)   // 解不存在
        {
            puts("0");
            continue;
        }
        
        int res = n;
        for(int start = 1; s[start - 1] + target <= s[n]; ++start)  //枚举区间起点 
        {
            int end = lower_bound(s + start, s + n + 1, s[start - 1] + target) - s; //二分终点 
            res = min(res, end - start + 1);
        }
        
        printf("%d\n", res);
    }
    
    return 0;
}

尺取法

我们设以 开始、总和最初大于 时的连续子序列为 ,从 开始总和最初超过 的连续子序列如果是 的话,则必然有 。

利用这一性质便可以设计出如下算法:

  1. 以 初始化。
  2. 只要依然有 ,就不断将 增加 ,并将 增加 。
  3. 如果(2)中无法满足 则终止。否则的话,更新 。
  4. 将 减去 ,增加 然后回到(2)。

对于这个算法,因为 最多变化 次,因此只需 的复杂度就可以求解这个问题了。

image.png
#include
#include
#include
#include

using namespace std;
const int N = 100010;
int a[N];

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int n, target;
        scanf("%d%d", &n, &target);
        for(int i = 0; i < n; i++)  scanf("%d", &a[i]);
        
        int res = 1e9;
        int s = 0, t = 0, sum = 0;
        
        while(true)
        {
            while(t < n && sum < target)    sum += a[t++];          
            if(sum < target)    break;
            res = min(res, t - s);
            sum -= a[s++];
        }
        
        if(res > n) res = 0;    //解不存在 
        
        printf("%d\n", res);
    }
    
    return 0;
}

Codeforces 1263A Sweet Problem

题目链接:点击这里

#include
#include
#include

using namespace std;
int a[5];

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        for(int i = 0; i < 3; i++)  scanf("%d", &a[i]);
        
        sort(a, a + 3);
        
        int ans = a[0];         // 先吃完最小堆a[0] 
        
        if(a[2] - a[0] >= a[1]) // 若最大堆a[2]足够抵消最小堆a[0] 
        {
            ans += a[1];
        }
        else                    // 若最大堆a[2]不足以抵消最小堆a[0],那就把a[2]吃到和a[1]相等 
        {
            int t = a[0] - (a[2] - a[1]);
            if(t & 1)   ans += a[1] - t / 2 - 1;
            else    ans += a[1] - t / 2;
        }
        
        printf("%d\n", ans);
    }
    
    return 0;
}

你可能感兴趣的:(2020年9月21日(完))