从今天起,cf开刷,先从难度1600写起试试,每个难度的题目放在一个博客里面。看看最后可以写多少个博客。每一道AC的题目的代码都放在这里 😃
1600打算写25道题后晋级1700.
最近训练重心:数学
math、number theory、probabilities、geometry、combinatorics
每天至少3道题:见到这几个标签就去写!
1.C. k-Tree
- 太菜了,不会写。官方的题解写的挺好的。
- f ( i , j ) f(i, j) f(i,j):路径权重总和是 i,j = 0 表示路径中没有超过 d 的边,j = 1 表示路径中有超过 d 的边。
- 我们这样思考,对于计算 f ( i , 0 ) f(i, 0) f(i,0), 我们让这个分为两步,先走 k,再一步走了 n - k,因此 f ( i , 0 ) = f ( i − 1 , 0 ) + f ( i − 2 , 0 ) + . . . + f ( i − m i n ( d − 1 , i ) , 0 ) f(i, 0) = f(i - 1, 0) + f(i - 2, 0) + ... + f(i - min(d - 1, i), 0) f(i,0)=f(i−1,0)+f(i−2,0)+...+f(i−min(d−1,i),0)。这样子首先是不会重复的,因为最后一步走的距离不一样,肯定不会一样。这样子应该也不会有遗漏的吧,因为…想不到会遗漏哪种情况,最后一步都枚举过了。
- f ( i , 1 ) = f ( i − 1 , 1 ) + . . . + f ( i − m i n ( i , k ) , 1 ) + f ( i − d , 0 ) + f ( i − ( d + 1 ) , 0 ) + . . . + f ( i − m i n ( k , i ) , 0 ) f(i, 1) = f(i - 1, 1) + ... + f(i - min(i, k), 1) + f(i - d, 0) + f(i - (d + 1), 0) + ... + f(i - min(k, i), 0) f(i,1)=f(i−1,1)+...+f(i−min(i,k),1)+f(i−d,0)+f(i−(d+1),0)+...+f(i−min(k,i),0).
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 110;
int f[maxn][maxn], N, K, D;
const int mod = 1e9 + 7;
int main(){
scanf("%d%d%d", &N, &K, &D);
f[0][0] = 1;
for(int i = 0; i <= N; i++){
for(int j = 1; j <= min(D - 1, i); j++){
f[i][0] = (f[i][0] + f[i - j][0]) % mod;
}
}
for(int i = 0; i <= N; i++){
for(int j = 1; j <= min(i, K); j++){
f[i][1] = (f[i][1] + f[i - j][1]) % mod;
}
for(int j = D; j <= min(i, K); j++){
f[i][1] = (f[i][1] + f[i - j][0]) % mod;
}
}
printf("%d\n", f[N][1]);
return 0;
}
2.A. Linova and Kingdom
- 太菜了,不会写。但是官方的题解写的挺好懂的。
- 答案思路没问题,但是公式写错了。我说怎么理解不了那个公式!
- 应该这么说:the total happiness will be increased by s i z u − d e p u − 1 siz_u−dep_u - 1 sizu−depu−1: the envoy of u won’t be sent, we will lose d e p u dep_u depu happiness; a total of s i z u − 1 siz_u−1 sizu−1 envoys of all nodes in the subtree rooted on u except u itself will get one more happiness.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<functional>
using namespace std;
const int maxn = 200010, maxm = maxn * 2;
typedef long long ll;
int h[maxn], e[maxm], ne[maxm], idx;
int N, K;
int d[maxn], happiness[maxn];
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int dfs(int u, int fa){
int res = 1;
for(int i = h[u]; i != -1; i = ne[i]){
int v = e[i];
if(v == fa) continue;
d[v] = d[u] + 1;
res += dfs(v, u);
}
happiness[u] = res - d[u] - 1;
return res;
}
int main(){
scanf("%d%d", &N, &K);
memset(h, -1, sizeof h);
for(int i = 1; i < N; i++){
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
dfs(1, -1);
sort(happiness + 1, happiness + N + 1, greater<int>());
ll ans = 0;
for(int i = 1; i <= N - K; i++) ans += happiness[i];
printf("%lld\n", ans);
return 0;
}
3.A. Maze
这道题,我想这是什么玩意儿,想半天不知道怎么写。然后看一下题解…😂
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int N, M, K;
const int maxn = 510;
char maze[maxn][maxn];
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
int cnt, blank;
void dfs(int x, int y){
maze[x][y] = '*';
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(nx < 0 || nx >= N || ny < 0 || ny >= M || maze[nx][ny] != '.') continue;
if(++blank <= cnt - K) dfs(nx, ny);
}
}
int main(){
scanf("%d%d%d", &N, &M, &K);
for(int i = 0; i < N; i++){
scanf("%s", maze[i]);
}
for(int i = 0; i < N; i++){
for(int j = 0; j < M; j++){
if(maze[i][j] == '.') cnt++;
}
}
bool flag = false;
for(int i = 0; i < N; i++){
for(int j = 0; j < M; j++){
if(maze[i][j] == '.' && ++blank <= cnt - K){
dfs(i, j);
flag = true;
break;
}
}
if(flag) break;
}
for(int i = 0; i < N; i++){
for(int j = 0; j < M; j++){
if(maze[i][j] == '*') maze[i][j] = '.';
else if(maze[i][j] == '.') maze[i][j] = 'X';
}
}
for(int i = 0; i < N; i++) printf("%s\n", maze[i]);
return 0;
}
4.奇怪的八进制
对于这样的n进制,其实可以这样。十进制数x:
- stack.push((x - 1) % 8)
- x = (x - 1) / 8.
dic = {0:'A', 1:'B', 2:'C', 3:'D', 4:'E', 5:'F', 6:'G', 7:'H'};
def get(x):
if(x > 0):
get((x - 1) // 8);
print(dic[(x - 1) % 8], end = '');
x = int(input());
get(x);
5.D. Constructing the Array
- 给定一个长度为n的全0数组a,每次进行以下操作直到所有元素均不为零:在第i次操作中,取最长的全为0的一段子序列(优先取最左边的),令 a [ l e n / 2 ] = i a[len/2]=i a[len/2]=i 。其中len为偶数,取l+r或l+r−1。
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
struct P {
int l, r;
P(int l_ = 0, int r_ = 0): l(l_), r(r_){}
bool operator < (const P& rhp)const {
return r - l < rhp.r - rhp.l || r - l == rhp.r - rhp.l && l > rhp.l;
}
};
const int maxn = 200010;
int a[maxn], N;
priority_queue<P> que;
int main(){
int T;
scanf("%d", &T);
while(T--){
scanf("%d", &N);
que.push(P(0, N - 1));
int cnt = 0;
while(que.size()){
auto p = que.top(); que.pop();
int mid = (p.r + p.l) / 2;
a[mid] = ++cnt;
if (mid - 1 >= p.l) {
que.push(P(p.l, mid - 1));
}
if (p.r >= mid + 1) {
que.push(P(mid + 1, p.r));
}
}
for(int i = 0; i < N; i++){
printf("%d%c", a[i], i + 1 == N ? '\n' : ' ');
}
}
return 0;
}
6.C. Game On Leaves
- 我想,如果你想等到X变成叶子节点的时候再去取,那么是两个情况:它本身就是叶子节点;原图只剩下两个节点了。因为那两个人会想方设法不让它成为叶子节点。
- 小心变量别整错。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1010, maxm = 2010;
int N, X, deg[maxn];
int main(){
int T;
scanf("%d", &T);
while(T--){
scanf("%d%d", &N, &X);
memset(deg, 0, sizeof deg);
for(int i = 1; i < N; i++){
int a, b;
scanf("%d%d", &a, &b);
deg[a]++, deg[b]++;
}
if(deg[X] <= 1 || (N - 1) & 1) printf("Ayush\n");
else printf("Ashish\n");
}
return 0;
}
7.C. Circle of Monsters
- 从大角度分析。首先,怪物如果死后不爆炸,那么需要 A = ∑ i = 0 N − 1 a i A = \sum\limits_{i=0}^{N - 1}a_i A=i=0∑N−1ai 个子弹。但是,由于爆炸,可以节省 B = ∑ i = 0 N − 1 m i n ( G [ i ] . b , G [ ( i + 1 ) % N ] . a ) − x B = \sum\limits_{i=0}^{N-1}min(G[i].b, G[(i+1)\%N].a) - x B=i=0∑N−1min(G[i].b,G[(i+1)%N].a)−x 的子弹。A 和 B 都是定值,因此x越小越好。于是程序如下:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 300010;
typedef long long ll;
struct P{
ll a, b;
}G[maxn];
int N;
int main(){
int T;
scanf("%d", &T);
while(T--){
scanf("%d", &N);
for(int i = 0; i < N; i++){
scanf("%lld%lld", &G[i].a, &G[i].b);
}
ll A = 0, B = 0, max_B = 1e18;
for(int i = 0; i < N; i++){
A += G[i].a, B += min(G[i].b, G[(i + 1) % N].a);
max_B = min(min(G[i].b, G[(i + 1) % N].a), max_B);
}
printf("%lld\n", A - B + max_B);
}
return 0;
}
8.G. Special Permutation
- 无脑构造题,挺好写的。方法略微有点麻烦,但是解决问题第一步嘛。
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1010;
int a[maxn], N;
int dx[5][10] = { {1, 3, 5, 2, 4}, {1, 3, 6, 4, 2, 5}, {1, 3, 7, 4, 6, 2, 5},
{1, 3, 5, 7, 4, 8, 6, 2}, { 1, 3, 5, 7, 9, 6, 8, 4, 2 } };
int main(){
int T;
scanf("%d", &T);
while(T--){
scanf("%d", &N);
int m1 = N / 5, m2 = N % 5;
bool flag = true;
if(N == 2 || N == 3){
flag = false;
}
else if(N == 4){
a[0] = 2, a[1] = 4, a[2] = 1, a[3] = 3;
}
else{
for (int i = 0; i < (m1 - 1) * 5; i++) {
int k1 = i / 5, k2 = i % 5;
a[i] = k1 * 5 + dx[0][k2];
}
for(int i = (m1 - 1) * 5; i < N; i++){
a[i] = dx[m2][i - (m1 - 1) * 5] + (m1 - 1) * 5;
}
}
if(!flag){
printf("-1\n");
continue;
}
for(int i = 0; i < N; i++){
printf("%d%c", a[i], i + 1 == N ? '\n' : ' ');
}
}
return 0;
}
9.C. Good Subarrays
你需要找到有多少个区间使得其区间内每个元素的和为区间长度。
- Let’s precalculate the array p, p r − p l − 1 = r − l + 1 ↔ p r − r = p l − 1 − ( l − 1 ) p_r - p _{l-1} = r - l + 1 \leftrightarrow p_r - r = p_{l - 1} - (l - 1) pr−pl−1=r−l+1↔pr−r=pl−1−(l−1).
- 别忘记 p 0 p_0 p0 也要算上。
#include<cstdio>
#include<iostream>
#include<unordered_map>
#include<cstring>
using namespace std;
const int maxn = 100010;
int N, a[maxn];
unordered_map<int, int> cnt;
typedef long long ll;
int main() {
int T;
cin >> T;
while (T--) {
cnt.clear();
cnt[0] = 1;
char c;
cin >> N;
for (int i = 1; i <= N; i++) {
cin >> c;
a[i] = c - '0';
a[i] += a[i - 1];
cnt[a[i] - i]++;
}
ll ans = 0;
for (auto p : cnt) {
ll x = p.second;
ans += (ll)x * (x - 1) / 2;
}
cout << ans << endl;
}
return 0;
}
10.C. Phoenix and Distribution
- 给了一个长度为n的字符串,让你将字符串分成k个字符串,这k个字符串不能存在空串,现在想要知道,这k个字符串中 字典序最大的最小的字符串是什么。
- 设最小的字符是 x,x 出现的次数是 cnt. 如果 cnt < K,那么就输出字典序第 K 小的字符;cnt >= K时,看看剩下的 N - K 个字符是否相等,如果相等,则均匀的分在每个字串后面,如果不相等,就全部接在 x 的后面。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<unordered_map>
using namespace std;
const int maxn = 100010;
char s[maxn];
int N, K;
bool all_the_same(const char* s) {
for (int i = 0; s[i]; i++) {
if (s[i] != s[0]) return false;
}
return true;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
unordered_map<char, int> cnt;
char minc = 'z';
scanf("%d%d%s", &N, &K, &s);
s[N] = 0;
for (int i = 0; i < N; i++) {
cnt[s[i]]++;
if (minc > s[i]) minc = s[i];
}
sort(s, s + N);
if (cnt[minc] < K) {
printf("%c\n", s[K - 1]);
continue;
}
else {
printf("%c", minc);
if (!all_the_same(s + K)) printf("%s\n", s + K);
else {
int left = strlen(s + K);
for (int i = 0; i < (left + K - 1) / K; i++) {
printf("%c", s[K]);
}
printf("\n");
}
}
}
return 0;
}
11.C. Yet Another Counting Problem
- 题意:Calculate the number of integers x x x such that l i ≤ x ≤ r i l_i≤x≤r_i li≤x≤ri, and ( ( x m o d a ) m o d b ) ≠ ( ( x m o d b ) m o d a ) ((x\ mod\ a)\ mod\ b)≠((x\ mod\ b)\ mod\ a) ((x mod a) mod b)=((x mod b) mod a).
- 本来想推到数学公式,然后发现不会推。但是 a 和 b 的范围都挺小的。打个表很快发现规律,以 L C M ( a , b ) LCM(a, b) LCM(a,b) 为循环节,每个循环节内满足条件的数都相同,算出[1,r]满足条件的数,算出[1,l−1]满足条件的数,相减即可。又是这个方法!因为查询数很多,所以可以算出前缀和数组。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn = 400010;
int res[maxn];
ll gcd(ll a, ll b) {
if (b == 0) return a;
return gcd(b, a % b);
}
ll A, B, lcm;
ll cal(ll x) {
return x / lcm * res[lcm] + res[x % lcm];
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
memset(res, 0, sizeof res);
scanf("%lld%lld", &A, &B);
lcm = A * B / gcd(A, B);
for (int i = 1; i <= lcm; i++) {
if (i % A % B != i % B % A) res[i] = 1;
res[i] += res[i - 1];
}
int Q;
scanf("%d", &Q);
for(int i = 0; i < Q; i++){
ll l, r;
scanf("%lld%lld", &l, &r);
printf("%lld%c", cal(r) - cal(l - 1), i + 1 == Q ? '\n' : ' ');
}
}
return 0;
}
12.C. Hard problem
- 要使n个字符串变为字典序。只能进行一种操作:将第i个字符串reverse,并消耗c[i]的能量。求消耗的最小的能量
- f ( i , j ) f(i, j) f(i,j): 前 i i i 个字符串, j = 0 j = 0 j=0 表示第 i i i 个字符串没有反转, j = 1 j = 1 j=1表示字符串反转。其实可以当做一个状态机去写。
- 然后就很好写了呀。不过有一个需要说清楚,按照字典序排序,可以出现小于等于的情况,不需要严格递增。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100010;
string nor[maxn], rev[maxn];
typedef long long ll;
ll c[maxn], f[maxn][2];
int N;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int main() {
cin >> N;
for (int i = 1; i <= N; i++) {
cin >> c[i];
}
for (int i = 1; i <= N; i++) {
cin >> nor[i];
rev[i] = nor[i];
reverse(rev[i].begin(), rev[i].end());
}
memset(f, 0x3f, sizeof f);
f[0][0] = f[0][1] = 0;
for (int i = 1; i <= N; i++) {
if (nor[i] >= nor[i - 1]) f[i][0] = min(f[i][0], f[i - 1][0]);
if (nor[i] >= rev[i - 1]) f[i][0] = min(f[i][0], f[i - 1][1]);
if (rev[i] >= nor[i - 1]) f[i][1] = min(f[i][1], f[i - 1][0] + c[i]);
if (rev[i] >= rev[i - 1]) f[i][1] = min(f[i][1], f[i - 1][1] + c[i]);
}
ll ans = min(f[N][0], f[N][1]);
if (ans == INF) cout << -1 << endl;
else cout << ans << endl;
return 0;
}
13.A. Orac and LCM
- 一个长度为 N 的数组,求 g c d { l c m ( { a i , a j } ) ∣ i < j } gcd\{lcm(\{a_i , a_j\}) | i < j\} gcd{lcm({ai,aj})∣i<j}
- Observation. p k ∣ a n s p^k ∣ ans pk∣ans if and only if there are at least n − 1 n−1 n−1 integers in a that s.t. p k ∣ a i p^k∣ a_i pk∣ai.
- g c d ( l c m ( a 1 , a 2 ) , l c m ( a 1 , a 3 ) , l c m ( a 1 , a 4 ) , … , l c m ( a 1 , a n ) ) gcd(lcm(a_1,a_2),lcm(a_1,a_3),lcm(a_1,a_4),…,lcm(a_1,a_n)) gcd(lcm(a1,a2),lcm(a1,a3),lcm(a1,a4),…,lcm(a1,an))。因为都有一个共同因子 a 1 a_1 a1。所以 r e s 1 = l c m ( a 1 , g c d ( a 2 , a 3 , a 4 , … , a n ) res_1=lcm(a_1,gcd(a_2,a_3,a_4,…,a_n) res1=lcm(a1,gcd(a2,a3,a4,…,an)。同理 g c d 2 gcd_2 gcd2 也是这样的。最终答案是 g c d ( r e s 1 , r e s 2 , r e s 3 , … , r e s n ) gcd(res_1,res_2,res_3,…,res_n) gcd(res1,res2,res3,…,resn)。所以我们只要求出一个后缀gcd即可.
- 其实还有一种方法,对每个数进行质因数分解,因为 a i a_i ai 的范围比较小。然后选出次数第二大的k作为 p k ∣ a n s p^k | ans pk∣ans. 不过要特判一种情况。当有两个 a i a_i ai 都不能被 p 整除的时候,就把这个p舍去。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 100010;
int N;
ll a[maxn], GCD[maxn], res[maxn];
ll gcd(ll a, ll b) {
if (b == 0) return a;
return gcd(b, a % b);
}
ll lcm(ll a, ll b, ll g) {
return a * b / g;
}
int main() {
cin >> N;
for (int i = 1; i <= N; i++) {
cin >> a[i];
}
for (int i = N; i >= 1; i--) {
GCD[i] = gcd(a[i], GCD[i + 1]);
}
for (int i = 1; i < N; i++) {
res[i] = lcm(a[i], GCD[i + 1], gcd(a[i], GCD[i + 1]));
}
ll ans = res[1];
for (int i = 2; i < N; i++) {
ans = gcd(ans, res[i]);
}
cout << ans << endl;
}
14.C. Game with Chips
- 2 ∗ N ∗ M 2*N*M 2∗N∗M 是一个非常大的数,因此可以先把所有chips移动到同一个格子2333…
- 第一步,先把所有chips移动到 ( 1 , 1 ) (1,1) (1,1),然后把格子全部访问一遍就行。不过这个是不可以把目标点排序然后依次移动过去的。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxk = 210;
vector<char> ans;
int N, M, K;
int main() {
scanf("%d%d%d", &N, &M, &K);
for (int i = 0; i < 2 * K; i++) {
int x, y;
scanf("%d%d", &x, &y);
}
for (int i = 1; i < N; i++) ans.push_back('U');
for (int i = 1; i < M; i++) ans.push_back('L');
for (int i = 1; i <= N; i++) {
if (i & 1) {
for (int j = 1; j < M; j++) {
ans.push_back('R');
}
}
else {
for (int j = 1; j < M; j++) {
ans.push_back('L');
}
}
ans.push_back('D');
}
ans.pop_back();
cout << ans.size() << endl;
for (auto u : ans) cout << u;
cout << endl;
return 0;
}
15.B. Array Walk
题意:有1-n个点,每个点赋值为a[i],现在可以移动k次,不过只能向左移动z次,并且不能连续向左移动,问你最后走过所有点的和最大是多少。
- 最终的位置由向左走的步数决定。假设向左走了 t t t 步,则向右走了 k − t k - t k−t 步。最后一定呆在 k − 2 ∗ t + 1 k - 2 * t + 1 k−2∗t+1。就是把 1 1 1 到 k − 2 ∗ t + 1 k - 2 * t + 1 k−2∗t+1 加起来,然后在 1 1 1 到 k − 2 ∗ t + 2 k - 2 * t + 2 k−2∗t+2 中找到最大的 ( a i − 1 , a i ) (a_{i-1},a_{i}) (ai−1,ai)。为什么是 k − 2 ∗ t + 2 k - 2 * t + 2 k−2∗t+2 呢?因为可能最后最大的 pair 是 ( a k − 2 ∗ t + 1 , a k − 2 ∗ t + 2 ) (a_{k - 2 * t + 1}, a_{k - 2 * t + 2}) (ak−2∗t+1,ak−2∗t+2).
- 其实可以先扫描一遍,维护一个前缀数组, r e s i res_i resi 为 a k − 1 + a k a_{k - 1} + a_{k} ak−1+ak 最大值,其中 k ≤ i k\le i k≤i. 当然还有一个前缀和数组。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 100010;
int a[maxn], res[maxn];
int N, K, Z;
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &N, &K, &Z);
for (int i = 1; i <= N; i++) scanf("%d", &a[i]);
int max_now = 0;
for (int i = 1; i <= N; i++) {
if (a[i] + a[i - 1] > max_now) max_now = a[i] + a[i - 1];
res[i] = max_now;
}
for (int i = 1; i <= N; i++) {
a[i] += a[i - 1];
}
int ans = 0;
for (int t = 0; t <= Z; t++) {
if (K - 2 * t + 1 < 1) break;
int tmp = a[K - 2 * t + 1] + t * res[K - 2 * t + 2];
ans = max(ans, tmp);
}
cout << ans << endl;
}
return 0;
}
16.A. LCM Challenge
- 题意:找到3个不超过n的正整数(可以相同),使得它们的lcm(最小公倍数)最大。输出最大的lcm。
- 找规律我们发现,对于任意一个正整数 a:a 和 a + 1 最大公因数都是1;当 a 是奇数的时候,a 和 a + 2 最大公因数是1;当 a 是偶数的时候,a 和 a + 2 最大公因数是2,a 和 a + 3 的最大公约数是 1 或者是 3,因此,当 N > 6 时, 则 ( N − 1 ) ∗ ( N − 2 ) ∗ ( N − 3 ) (N - 1) * (N - 2) * (N - 3) (N−1)∗(N−2)∗(N−3) 比 N ∗ ( N − 1 ) ∗ ( N − 2 ) / 2 N * (N - 1) * (N - 2) / 2 N∗(N−1)∗(N−2)/2 应该更大一些。
- 题解的方法还是有点😂,只看 [max(1, n - 50), n]中间连续的三个数最大公因数是多少,持续更新答案。
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll N;
ll gcd(ll a, ll b) {
if (b == 0) return a;
return gcd(b, a % b);
}
int main() {
cin >> N;
if (N == 1) cout << 1 << endl;
else if (N == 2) cout << 2 << endl;
else if (N & 1) cout << N * (N - 1) * (N - 2) << endl;
else {
ll ans = max(N * (N - 1) * (N - 2) / 2, N * (N - 1) * (N - 3) / gcd(N, N - 3));
ans = max((N - 1) * (N - 2) * (N - 3), ans);
cout << ans << endl;
}
return 0;
}
//int main() {
// for (ll i = 6; i <= 1000; i+=2) {
// printf("%lld %lld %lld\n", i, i - 3, gcd(i, i - 3));
// }
//}
17. A. Multiples of Length
- 题意:给定一个数列{a},每次你需要选中一个区间[L,R],然后对区间内每个数字进行一次操作,让ai变成ai+k,并且满足k mod (R−L+1)=0.你只能严格做三遍操作,使得最终所有数字变成0. 注意,不是说加上同一个数,区间内每个数字加上的数不需要相等。
- 我就知道是这个味,但还是没有想到。其实,因为数不相同,所以加上的数字,集合区间长度联系上,有和 a i a_i ai 联系上,那就是这个方法了。
- 把每一个数加上 − N ∗ a i -N* a_i −N∗ai 就是 − ( N − 1 ) ∗ a i -(N - 1) * a_i −(N−1)∗ai,然后每个数再加上 ( N − 1 ) ∗ a i (N - 1)*a_i (N−1)∗ai 就好了。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 100010;
int a[maxn], N;
int main() {
cin >> N;
for (int i = 1; i <= N; i++) cin >> a[i];
if (N == 1) {
printf("1 1\n0\n1 1\n0\n1 1\n%d\n", -a[1]);
}
else {
printf("1 1\n%d\n", -a[1]);
printf("1 %d\n", N);
a[1] = 0;
for (int i = 1; i <= N; i++) {
printf("%lld%c", -(ll)N * (ll)a[i], i == N ? '\n' : ' ');
}
printf("2 %d\n", N);
for (int i = 2; i <= N; i++) {
printf("%lld%c", (ll)(N - 1) * (ll)a[i], i == N ? '\n' : ' ');
}
}
return 0;
}
18. C. Kuroni and Impossible Calculation
- 题意:给定n个整数和m,求 ∏ 1 ≤ i < j ≤ n ∣ a i − a j ∣ m o d ( m ) ∏_{1≤i<j≤n}|a_i−a_j|mod(m) ∏1≤i<j≤n∣ai−aj∣mod(m) 的值范围。性质: 2 ≤ n ≤ 2 × 1 0 5 , 1 ≤ m ≤ 1 0 3 , 0 ≤ a i ≤ 1 0 9 2≤n≤2×10^5,1≤m≤10^3,0≤a_i≤10^9 2≤n≤2×105,1≤m≤103,0≤ai≤109
- 你发现 M 很小,最后结果要取 M,所以当 N > M 时,根据鸽巢原理,答案一定是0呀。
- N <= M 时,直接暴力求解就行。
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 200010;
typedef long long ll;
int N;
ll a[maxn], M;
int main() {
cin >> N >> M;
for (int i = 1; i <= N; i++) {
cin >> a[i];
}
if (N > M) {
cout << 0 << endl;
}
else {
ll ans = 1;
for (int i = 1; i <= N; i++) {
for (int j = i + 1; j <= N; j++) {
ans = ans * abs(a[i] - a[j]) % M;
}
}
cout << ans << endl;
}
return 0;
}
19. B. Ciel and Flowers
- 这道题从剩下几朵花讨论会比较方便
- 其实只会出现六种情况:000,001,002,011,012,022;最后一种需要注意是否需要把一束纯色花拆开,其他都不用管。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> P;
P R, G, B;
vector<P> v;
int main() {
cin >> R.second >> G.second >> B.second;
R.first = R.second % 3, G.first = G.second % 3, B.first = B.second % 3;
v.push_back(R), v.push_back(G), v.push_back(B);
sort(v.begin(), v.end());
ll res = R.second + G.second + B.second;
while (v[0].first >= 1 && v[1].first >= 1 && v[2].first >= 1) {
v[0].first--;
v[1].first--;
v[2].first--;
}
if (v[0].first == 0 && v[1].first == 2 && v[2].first == 2 && v[0].second >= 3) {
cout << ((res - 1) / 3) << endl;
}
else {
cout << ((res - v[0].first - v[1].first - v[2].first) / 3) << endl;
}
return 0;
}
20.C. Two Arrays
- 题意:你被给予了两个整数n和m,计算两个数组对a,b的个数。数组需满足:
- 两个数组的长度都是m
- 数组中每一个整数都是1到n
- ai≤bi
- 数组a是一个非降的序列,数组b是一个非升的序列
- 可以考虑将a,b数组连起来形成一个b[1],b[2],…,b[m],a[m],a[m-1],…,a[1]的数组,长度为2m且非严格递减,取值都在[1,n]之间(与原问题等价)。
- 转化为 1 ≤ a 1 ≤ . . . ≤ a 2 m ≤ N 1 \le a_1 \le ... \le a_{2m} \le N 1≤a1≤...≤a2m≤N.
- 令 x 1 = a 1 , x 2 = a 2 − a 1 . . . , x 2 m = a 2 m − a 2 m − 1 x_1 = a_1, x_ 2 = a_2 - a_1..., x_{2m} = a_{2m} - a_{2m - 1} x1=a1,x2=a2−a1...,x2m=a2m−a2m−1,则 x 1 + x 2 + . . . + x 2 m ≤ N x_1 + x_2 + ... + x_{2m} \le N x1+x2+...+x2m≤N
- 令 z 1 = x 1 , z 2 = x 2 + 1 , . . . , z 2 m = x 2 m + 1 令 z_1 = x_1, z_2 = x_2 + 1, ..., z_{2m} = x_{2m} + 1 令z1=x1,z2=x2+1,...,z2m=x2m+1,则 0 < z 1 + z 2 + . . . + z 2 m ≤ N 0 < z_1 + z_2 + ... + z_{2m} \le N 0<z1+z2+...+z2m≤N
- 这样就转化为 2m 个隔板,N + 2m - 1 个空隙,答案就是 C N + 2 m − 1 2 m C_{N + 2m - 1}^{2m} CN+2m−12m
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
ll N, M;
ll mod_pow(ll x, ll n) {
ll res = 1;
while (n) {
if(n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
int main() {
cin >> N >> M;
ll ans = 1;
for (ll i = N + 2 * M - 1; i >= N; i--) {
ans = ans * i % mod;
}
for (ll i = 1; i <= 2 * M; i++) {
ans = ans * mod_pow(i, mod - 2) % mod;
}
cout << ans << endl;
}
21.C. Chocolate Bunny
- 交互题,询问 i , j i,j i,j 的 p i m o d p j p_i \ mod\ p_j pi mod pj 的值,问最后这个长度为n的排列是什么。最多问 2*n 次。
- 不要看漏是一个1~N的排列,不然就亏大了。
- a mod b < b mod a,说明 a > b;因为,当 a > b 时,a mod b <= b - 1,而 b % a = b.
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 10010;
int a[maxn], N;
int main() {
int max_id = 1;
scanf("%d", &N);
for (int i = 2; i <= N; i++) {
printf("? %d %d\n", i, max_id);
fflush(stdout);
int x1, x2;
scanf("%d", &x1);
printf("? %d %d\n", max_id, i);
fflush(stdout);
scanf("%d", &x2);
if (x1 > x2) {
a[i] = x1;
}
else if(x1 < x2){
a[max_id] = x2;
max_id = i;
}
}
a[max_id] = N;
printf("! ");
fflush(stdout);
for (int i = 1; i <= N; i++) {
printf("%d%c", a[i], i == N ? '\n' : ' ');
fflush(stdout);
}
return 0;
}
22.D2. Equalizing by Division (hard version)
- 给了n个数 每次操作可以任意选一个数对他除以2向下取整代替这个数,问让这n个数中,至少有k个数一样。至少要多少次操作. 1 ≤ n , k ≤ 2 ∗ 1 0 5 . 1 \le n,k \le 2*10^5. 1≤n,k≤2∗105.
- vector v[maxn], 记录把每一个元素变为 i 时的花费,push 进 v[i] 里面,然后找到满足 v[i].size() >= k 且总花费最小的。
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 200010;
vector<int> v[maxn];
int N, K;
int main() {
scanf("%d%d", &N, &K);
for (int i = 0; i < N; i++) {
int x;
scanf("%d", &x);
int cnt = 0;
while (x > 0) {
v[x].push_back(cnt);
x >>= 1;
cnt++;
}
v[0].push_back(cnt);
}
int ans = 1e9;
for (int i = 0; i <= 200000; i++) {
int res = 0;
if (v[i].size() >= K) {
sort(v[i].begin(), v[i].end());
for (int j = 0; j < K; j++) res += v[i][j];
ans = min(ans, res);
}
}
printf("%d\n", ans);
}
23.B. New Year Permutation
- 题目的意思是给n个数 a [ ] a[] a[],然后给 n ∗ n n*n n∗n 的关系矩阵 g [ ] [ ] g[][] g[][],如果关系矩阵中 a [ i ] [ j ] a[i][j] a[i][j] ==1,则代表a[i]和a[j]等价,可以交换。问经过交换能得到的字典序最小的序列为多少。
- 这道题其实不难,模拟的感觉强了一点,很顺利。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = 310;
int g[maxn][maxn], N, a[maxn], ans[maxn];
void floyd() {
for (int k = 1; k <= N; k++) {
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
g[i][j] |= (g[i][k] & g[k][j]);
}
}
}
}
int main() {
cin >> N;
for (int i = 1; i <= N; i++) {
cin >> a[i];
}
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
char c;
cin >> c;
g[i][j] = c - '0';
}
}
floyd();
for (int u = 1; u <= N; u++) {
int id;
for (id = 1; id <= N; id++) {
if (a[id] == u) break;
}
for (int i = 1; i <= N && i <= id; i++) {
if (g[i][id] == 1 && !ans[i]) {
ans[i] = u;
swap(a[i], a[id]);
break;
}
if (i == id) {
ans[i] = u;
}
}
}
for (int i = 1; i <= N; i++) {
printf("%d%c", ans[i], i == N ? '\n' : ' ');
}
return 0;
}
24.A. Little Pony and Expected Maximum
- 题意:给定m,表示有一个m面的色子,每个面的值为1~m,概率相同,丢n次,问说最大值的期望。
- 这道题蛮简单的。其实这种概率题都是可以转化为古典概型的啦。难点就在我们计算一个序列的个数(当序列中最大的元素是 i 时。
- 答案就是: ∑ i = 1 m i ∗ i n − ( i − 1 ) n m n = ∑ i = 1 m i ∗ [ ( i m ) n − ( i − 1 m ) n ] . \sum\limits_{i=1}^{m}i * \frac{i^n-(i-1)^n}{m^n}=\sum\limits_{i=1}^{m}i*[(\frac{i}{m})^n-(\frac{i-1}{m})^n]. i=1∑mi∗mnin−(i−1)n=i=1∑mi∗[(mi)n−(mi−1)n]. 等式的右边为了防止浮点数溢出。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
double qpow(double x, int n) {
double res = 1;
while (n) {
if(n & 1) res *= x;
x *= x;
n >>= 1;
}
return res;
}
int N, M;
int main() {
cin >> M >> N;
double ans = 0;
for (int i = 1; i <= M; i++) {
ans += i * (qpow((double)i / (double)M, N) - qpow(((double)(i - 1) / (double)M), N));
}
printf("%.15f\n", ans);
return 0;
}
25.A. Rational Resistance
- 这个题有个规律,就是a/b,b/a所需要的电阻数量一样,只是串并联关系不一样而已,因此该题可以这样考虑:那么把每一个电阻都拆成 a b \frac{a}{b} ba 与 a % b b \frac{a \% b}{b} ba%b.
- 为什么 a b \frac{a}{b} ba 和 b a \frac{b}{a} ab 所需电阻数量是一样的呢?If a fraction a b \frac{a}{b} ba can be obtained with k k k resistors, then it is simple to calculate that we can obtain a + b b \frac{a + b}{b} ba+b fractions and a a + b \frac{a}{a+b} a+ba with k + 1 k + 1 k + 1 resistors.
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll a, b;
int main() {
cin >> a >> b;
ll ans = 0;
while (b) {
ans += a / b;
a %= b;
swap(a, b);
}
cout << ans << endl;
}