01_数对

题目描述

牛牛以前在老师那里得到了一个正整数数对(x, y), 牛牛忘记他们具体是多少了。
但是牛牛记得老师告诉过他x和y均不大于n, 并且x除以y的余数大于等于k。
牛牛希望你能帮他计算一共有多少个可能的数对。

输入描述:
输入包括两个正整数n,k(1 <= n <= 10^5, 0 <= k <= n - 1)。

输出描述:
对于每个测试用例, 输出一个正整数表示可能的数对数量。

解题思路

x可以在 [1, n] 上取,但是y只能在 [k, n]上取,因为k以下都不存在大于等于k的余数。
所以遍历y,对于每一个y,统计符合的x的个数,加到count里。

先假设x可以从 [0, n]中取值,那么这段区间至少可以分成(n/k)个完整的、长度为y的区间。
x = 【0,1……y-2,y-1】【y,y+1,……,2y-2,2y-1】……【……】……【……,n】
在每个小区间a上,第i个数a[i]%y的余数是i。这样每一小段上大于等于k的x有y-k个(显然当k=0时,y个数都满足题意)。
【0,1,……,k,k+1,……,y-1】

这样,已经遍历的总数是图片说明 ,而其中满足条件的x的总数是

图片说明

因为图片说明
所以还没遍历的数有图片说明 个,令它为t。

因为n%y∈[0, y-1], 则t∈[1, y]。

也就是说我这种方法,至少剩了一个数,至多会把一个整区间(数量为y)都剩下来。
但是无论如何,这个区间last的第i个数last[i]%y一定是i。则最后一个数(n)的余数就是n%y。
如此一来,此区间内从 [k,n%y] 包含共计 n%y-k+1 个数。不过如果算出小于0的数,则不需要减回去,直接当没有就可以了。
所以最后一个区间里包含了 max(n%y-k+1, 0) 个满足条件的x。

java代码实现:

import java.util.Scanner;

    public class Main {
    public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    long n = sc.nextInt();
    long k = sc.nextInt();
    long count = 0;
    if (k == 0) {
        count = n * n;
        //任何一数对,取余时余数必定大于等于0
    } else {
        for (long y = k + 1; y <= n; y++) {
        count += (n / y) * (y - k) + Math.max(0, n % y - k + 1);
        // 假设n=10,k=3,则对y来说只能是4,5,6,7,8,9,10
        // 当y=4,(n/y)*(y-k)代表x小于等于8(8是4的整数倍)时有(3,4),(7,4),Math.max(0,n%y-k+1)代表x大于8时符合题意的对数为0
        // 当y=5,(n/y)*(y-k)代表x小于等于10(10是5的整数倍)时有(3,5),(4,5),(8,5),(9,5),Math.max(0,n%y-k+1)代表x大于10时符合题意的对数为0
        // 当y=6,(n/y)*(y-k)代表x小于等于6时有(3,6),(4,6),(5,6),Math.max(0,n%y-k+1)代表x大于6时符合题意的对数为2,分别是(9,6),(10,6)
        // 当y=7,(n/y)*(y-k)代表x小于等于7时有(3,7),(4,7),(5,7),(6,7),Math.max(0,n%y-k+1)代表x大于7时符合题意的对数为1,是(10,7)
        // ...以此类推
        }
    }
    System.out.println(count);
    }
}

02_翻转数列

题目描述

小Q定义了一种数列称为翻转数列:
给定整数n和m, 满足n能被2m整除。对于一串连续递增整数数列1, 2, 3, 4..., 每隔m个符号翻转一次, 最初符号为'-';。
例如n = 8, m = 2, 数列就是: -1, -2, +3, +4, -5, -6, +7, +8.
而n = 4, m = 1, 数列就是: -1, +2, -3, + 4.
小Q现在希望你能帮他算算前n项和为多少。

输入描述:
输入包括两个整数n和m(2 <= n <= 109, 1 <= m), 并且满足n能被2m整除。

输出描述:
输出一个整数, 表示前n项和。

解题思路

首先观察数列,我们可以将一组负正的数出现(如-1,-2,3,4)看做一组,则n个数一共有n/(2m)组,而每一组求和结果为m*m,
于是得到前n项和公式为

图片说明

java代码实现:

import java.util.Scanner;
public class 翻转数列 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        System.out.println(n * m / 2);
    }
}

03_纸牌游戏

题目描述

牛牛和羊羊正在玩一个纸牌游戏。这个游戏一共有n张纸牌, 第i张纸牌上写着数字ai。
牛牛和羊羊轮流抽牌, 牛牛先抽, 每次抽牌他们可以从纸牌堆中任意选择一张抽出, 直到纸牌被抽完。
他们的得分等于他们抽到的纸牌数字总和。
现在假设牛牛和羊羊都采用最优策略, 请你计算出游戏结束后牛牛得分减去羊羊得分等于多少。
输入描述:
输入包括两行。
第一行包括一个正整数n(1 <= n <= 105),表示纸牌的数量。
第二行包括n个正整数ai(1 <= ai <= 109),表示每张纸牌上的数字。
输出描述:
输出一个整数, 表示游戏结束后牛牛得分减去羊羊得分等于多少。

解题思路

牛牛和羊羊都采用最优策略!这一句很重要,说明他们每次都抽取的剩余纸牌中最大的一张。因为牛牛先抽,那么最大的必然在牛牛手里,第二大在羊羊手里,第三大又在牛牛手里,以此类推。只需要将纸牌数值组成的数组A从大到小排序,A[0]给牛牛,A[1]给羊羊,A[2]给牛牛.....将索引为偶数的项全部给牛牛,为奇数的项全部给羊羊。计算出偶数项和奇数项的差值即可。

java代码实现:

import java.util.Arrays;
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    int n = sc.nextInt();
    int A[] = new int[n];
    int A1[] = new int[n];
    for (int i = 0; i < n; i++) {
        A[i] = sc.nextInt();
    }
    Arrays.sort(A);
    for (int i =0; i<n; i++) {
        A1[i] = A[n-1-i];
    }
    long sum_n = 0;
    long sum_y = 0;
    for (int i = 0; i < n; i++) {
        if (i % 2 == 0) {
        sum_n += A1[i];
        } else
        sum_y += A1[i];
    }
        System.out.println(sum_n - sum_y);
    }
}