锐评

这次感觉还不错,相比于div2难度相差蛮大,找回些自信心。

A.Legs

题目大意

农夫约翰的农场又迎来了美好的一天。

农夫约翰来到农场后,数了数 ​n 条腿。众所周知,农场里只住着鸡和牛,鸡有 ​2 条腿,而牛有 ​4 条腿。

假设约翰农场主数清了所有动物的腿,那么他的农场里最少有多少动物?

输入描述

第一行包含一个整数 ​t ( ​1 \leq t \leq 10^3 ) - 测试用例数。

每个测试用例包含一个整数 ​n​2 \leq n \leq 2 \cdot 10^3​n 为偶数)。

输出描述

针对每个测试用例,输出一个整数,即农场主约翰的农场可以拥有的最少动物数量。

样例输入

3
2
6
8

样例输出

1
2
2

思路

暴力模拟即可,如果是4的倍数就全牛,否则就一个鸡其他全牛。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e6 + 9;
#define int long long
const int MOD = 998244353;
int qmi(int a, int b, int p) {
	int res = 1;
	while (b) {
		if (b & 1) res = res * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return res;
}
int inv(int x) {return qmi(x, MOD - 2, MOD);}

int gcd(int a, int b) {return b > 0 ? gcd(b, a % b) : a;}
int lcm(int a, int b) {return a / gcd(a, b) * b;}

int n, m;
void solve() {
	cin >> n;
    if (n == 2) {
        cout << 1 << "\n";
        return;
    }

    if (n % 4 == 0) cout << n / 4 << '\n';
    else cout << (n - 2) / 4 + 1 << "\n";
}

signed main() {
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int _ = 1;
	cin >> _;
	while (_--) {
		solve();
	}
	return 0;
}

B.Scale

题目大意

蒂娜有一个行数为 ​n 列数为​n的正方形网格。网格中的每个单元格要么是​0
要么是​1 。蒂娜希望将网格缩小 ​k 倍(**​k​n**的除数)。为此,蒂娜将网格分割成​k \times k 个不重叠的单元格块,使得每个单元格正好属于一个单元格块。

然后,蒂娜将每个单元格块替换为与单元格块中单元格值相等的单个单元格。保证同一区块中的每个单元格都具有相同的值

例如,下面的演示显示一个网格被缩小了 ​3 倍。

原始

​0 ​0 ​0 ​1 ​1 ​1
​0 ​0 ​0 ​1 ​1 ​1
​0 ​0 ​0 ​1 ​1 ​1
​1 ​1 ​1 ​0 ​0 ​0
​1 ​1 ​1 ​0 ​0 ​0
​1 ​1 ​1 ​0 ​0 ​0

缩小后

​0 ​1
​1 ​0

帮助 Tina 将网格缩小 ​k

输入描述

第一行包含一个整数 ​t ( ​1 \leq t \leq 100 ) —— 测试用例的数量。

对于每个测试用例:

  • 第一行包含两个整数 ​n​k ( ​1 \leq n \leq 1000, ​1 \le k \le n​k​n 的除数) —— 网格的行数和列数,以及蒂娜希望缩小网格的因子。
  • 接下来 ​n 行,每行包含 ​n 个字符(​0​1)—— 描述网格单元格的值。

保证每个 ​k \times k 的网格块都具有相同的值。
保证所有测试用例的 ​n 之和不超过 ​1000

输出描述

针对每个测试用例,在新的一行输出网格缩小了 ​k 倍的结果。

样例输入

4
4 4
0000
0000
0000
0000
6 3
000111
000111
000111
111000
111000
111000
6 2
001100
001100
111111
111111
110000
110000
8 1
11111111
11111111
11111111
11111111
11111111
11111111
11111111
11111111

样例输出

0
01
10
010
111
100
11111111
11111111
11111111
11111111
11111111
11111111
11111111
11111111

思路

按照要求模拟即可,每次枚举到列:列加k,枚举到行:行加k,用一个cnt记录一下答案的真实行数,每次行加k时,cnt++。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1009;
#define int long long
const int MOD = 998244353;
int qmi(int a, int b, int p) {
	int res = 1;
	while (b) {
		if (b & 1) res = res * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return res;
}
int inv(int x) {return qmi(x, MOD - 2, MOD);}

int gcd(int a, int b) {return b > 0 ? gcd(b, a % b) : a;}
int lcm(int a, int b) {return a / gcd(a, b) * b;}

int n, k;
string s[N], ans[N];
void solve() {
	cin >> n >> k;
    for (int i = 0; i < n; ++ i) 
        s[i].clear(), ans[i].clear();
	for (int i = 0; i < n; ++ i) {
        cin >> s[i];
    }
    int cnt = 0;
    for (int i = 0; i < n; i += k) {
        for (int j = 0; j < n; j += k) {
            ans[cnt] += s[i][j];
            // cout << i << " " << s[i] << "\n";
            // cout << j << " " << s[i][j] << "\n";
        }
        cnt ++;
    }
    for (int i = 0; i < n / k; ++ i) {
        cout << ans[i] << "\n";
    }
}

signed main() {
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int _ = 1;
	cin >> _;
	while (_--) {
		solve();
	}
	return 0;
}

C.Sort

题目大意

给定两个长度为 ​n 的字符串 ​a​b。然后,您(被迫)回答 ​q 个问题。

对于每个查询,您会得到一个由 ​l​r 限定的范围。在一次操作中,您可以选择一个整数 ​i ( ​l \leq i \leq r ),并设置 ​a_i = x 其中 ​x 是您想要的任何字符。在 ​\texttt{sorted(a[l..r])} = \texttt{sorted(b[l..r])} 条件下,输出必须执行的最少操作数。**对一个查询执行的操作不会影响其他查询。

对于任意字符串 ​c 来说, ​\texttt{sorted(c[l..r])} 表示由按字典序排列的字符 ​c_l, c_{l+1}, ..., c_r 组成的子串。

输入描述

第一行包含 ​t ( ​1 \leq t \leq 1000 ) —— 测试用例的数量。

每个测试用例的第一行包含两个整数 ​n​q ( ​1 \leq n, q \leq 2 \cdot 10^5 ) —— 两个字符串的长度和查询次数。

接下来一行包含长度为 ​n 的字符串 ​a。可以保证 ​a 只包含小写拉丁字母。

接下来一行包含长度为 ​n 的字符串 ​b。保证 ​b 只包含小写拉丁字母。

接下来的 ​q 行包含两个整数 ​l​r ( ​1 \leq l \leq r \leq n ) —— 查询范围。

保证所有测试用例中 ​n​q 的总和不超过 ​2 \cdot 10^5

输出描述

对于每个查询,在新行中输出一个整数,即需要执行的最少操作数。

样例输入

3
5 3
abcde
edcba
1 5
1 4
3 3
4 2
zzde
azbe
1 3
1 4
6 3
uwuwuw
wuwuwu
2 4
1 3
1 6

样例输出

0
1
0
2
2
1
1
0

样例说明

对于第一个查询, ​\texttt{sorted(a[1..5])} = abcde 和 ​\texttt{sorted(b[1..5])} = abcde,因此无需进行任何操作。

对于第二个查询,需要设置 ​a_1 = e,然后是 ​\texttt{sorted(a[1..4])} = \texttt{sorted(b[1..4])} = bcde。

思路

计算​26个字母在两个字符串中的数量前缀和,在之后的​l​r之间枚举两个字符串​26个字母的数量差,如果​a数组的一个字母的数量​t1小于​b数组的对应字母的数量​t2,那么应进行操作​t2 - t1,最后结果累加,注意内存不要超限。

#include<iostream>
using namespace std;
#define ll long long
const int N = 2e5 + 9;
string a, b;
int n, m;
int sta[N][30], stb[N][30];
void solve() {
    for (int i = 1; i <= n; ++ i) {
        for (int j = 0; j < 26; ++ j) {
            sta[i][j] = 0;
            stb[i][j] = 0;
        }
    }
    cin >> n >> m;
	cin >> a >> b;
    a = '?' + a;
    b = '?' + b;
    for (int i = 1; i <= n; ++ i) {
        sta[i][a[i] - 'a'] = sta[i - 1][a[i] - 'a'] + 1;
        stb[i][b[i] - 'a'] = stb[i - 1][b[i] - 'a'] + 1;
        for (int j = 0; j < 26; ++ j) {
            if (j != a[i] - 'a') sta[i][j] = sta[i - 1][j];
            if (j != b[i] - 'a') stb[i][j] = stb[i - 1][j];
        }
    }
    while (m --) {
        int l, r; cin >> l >> r;
        int dis = 0;
        for (int i = 0; i < 26; ++ i) {
            int t1 = sta[r][i] - sta[l - 1][i];
            int t2 = stb[r][i] - stb[l - 1][i];
            dis += t1 >= t2 ? t1 - t2 : 0;
        }
        cout << dis << "\n";
    }
}

signed main() {
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int _ = 1;
	cin >> _;
	while (_--) {
		solve();
	}
	return 0;
}

D.Fun

题目大意

给定两个整数 ​n​x,求满足 ​ab + ac + bc \le n​a + b + c \le x正整数三元组 ​(a, b, c) 的个数。

注意顺序问题(例如 ​(1, 1, 2)​(1, 2, 1) 被视为不同),且 ​a​b​c 必须严格大于 ​0

输入描述

第一行包含一个整数 ​t ( ​1 \leq t \leq 10^4 ) —— 测试用例的数量。

每个测试用例包含两个整数 ​n​x ( ​1 \leq n, x \leq 10^6 )。

保证所有测试用例的 ​n 之和不超过 ​10^6,所有测试用例的 ​x 之和不超过 ​10^6

输出描述

输出一个整数 —— 满足 ​ab + ac + bc \le n​a + b + c \le x 的正整数三元组 ​(a, b, c) 的个数。

样例输入

4
7 4
10 5
7 1000
900000 400000

样例输出

4
10
7
1768016938

样例说明

在第一个测试用例中,三元组是 ( ​1, 1, 1 )、( ​1, 1, 2 )、( ​1, 2, 1 ) 和 ( ​2, 1, 1 )。

在第二个测试案例中,三元组分别是 ( ​1, 1, 1 )、( ​1, 1, 2 )、( ​1, 1, 3 )、( ​1, 2, 1 )、( ​1, 2, 2 )、( ​1, 3, 1 )、( ​2, 1, 1 )、( ​2, 1, 2 )、( ​2, 2, 1 ) 和 ( ​3, 1, 1 )。

思路

实际上通过剪枝这道题可以暴力去做,​a​b都从​1​x-2​a+b>=x时剪掉,​ab+c*(a+b)<=n,其中​c>=1,所以​ab+a+b>=n时剪掉。剩下的​c二分出最大值,小于等于最大值的一定都符合,所以​ans+=最大值。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e6 + 9;
#define int long long
const int MOD = 998244353;
int qmi(int a, int b, int p) {
	int res = 1;
	while (b) {
		if (b & 1) res = res * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return res;
}
int inv(int x) {return qmi(x, MOD - 2, MOD);}

int gcd(int a, int b) {return b > 0 ? gcd(b, a % b) : a;}
int lcm(int a, int b) {return a / gcd(a, b) * b;}

int n, x;
void solve() {
	cin >> n >> x;
    int ans = 0;
    for (int i = 1; i <= x - 2; ++ i) {
        for (int j = 1; j <= x - 2 && i + j <= x; ++ j) {
            if (i * j + i + j > n) break;
            int l = 1, r = x - i - j;
            while(l < r) {
                int mid = l + r + 1 >> 1;
                if (i * j + j * mid + i * mid <= n) l = mid;
                else r = mid - 1;
            }
            ans += r;
        }
    }
    cout << ans << "\n";
}

signed main() {
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int _ = 1;
	cin >> _;
	while (_--) {
		solve();
	}
	return 0;
}