Codeforces Round 1017 (Div. 4)


A. Trippi Troppi

题意:给你三个字符串,将他们的首字母一起输出。

点击查看代码
void solve() {
	std::string a, b, c;
	std::cin >> a >> b >> c;
	std::cout << a[0] << b[0] << c[0] << "\n";
}

B. Bobritto Bandito

题意:一开始\([0, 0]\)被感染,每天被感染的区间\([l, r]\)会变成\([l - 1, r]\)或者\([l, r + 1]\)。现在第\(n\)的情况是\([l, r]\),求第\(m\)天的一个可能情况。

我们需要将区间缩小\(n - m\)个点。只要这个区间被\([l, r]\)包含且包含\(0\)就行。

点击查看代码
void solve() {
    int n, m, l, r;
    std::cin >> n >> m >> l >> r;
    m = n - m;
    int k = std::min(r, m);
    r -= k;
    m -= k;
    k = std::min(-l, m);
    l += k;
    std::cout << l << " " << r << "\n";
}

C. Brr Brrr Patapim

题意:有一个\(2 \times n\)的排列,它生成了一个\(n \times n\)的矩阵,矩阵的第\((i, j)\)个位置要么是\(p_{i + j}\)要么是\(p_{p_{i+j}}\)。现在给你这个矩阵,求一个可能的排列。

不是很懂这个题,直接用矩阵的右上角构造\(p[i + j] = a[i][j]\)过了。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector a(n + 1, std::vector<int>(n + 1));
    for (int i = 1; i <= n; ++ i) {
    	for (int j = 1; j <= n; ++ j) {
    		std::cin >> a[i][j];
    	}
    }

    std::vector<int> ans(2 * n + 1);
    std::set<int> s;
    for (int i = 1; i <= 2 * n; ++ i) {
    	s.insert(i);
    }
    for (int i = 1; i <= n; ++ i) {
    	for (int j = i; j <= n; ++ j) {
    		ans[i + j] = a[i][j];
    		s.erase(a[i][j]);
    	}
    }

    for (int i = 1; i <= 2 * n; ++ i) {
    	if (ans[i] == 0) {
    		ans[i] = *s.begin();
    		s.erase(s.begin());
    	}
    	std::cout << ans[i] << " \n"[i == 2 * n];
    }
}

D. Tung Tung Sahur

题意:给你一个字符串\(s\),只包含'L', 'R'。每个字符可能把自己复制一份放到自己后面。再给你一个\(t\),判断\(s\)能不能变成\(t\)

从后往前做,维护两个指针\(i, j\)表示\(s, t\)当前匹配到的位置。每次判断对应位置是不是相等,记一个\(cnt\)表示可以复制\(cnt\)个当前字符,那么如果\(s[i] \ne t[j]\),那么如果\(cnt \geq 1\)就可以让前面复制一个,然后\(cnt -= 1\)。否则无解。如果\(s[i] = t[j]\),判断\(t[i]\)是不是等于\(t[i-1]\),如果相等,那么\(cnt = cnt + 1\),否则\(cnt = 1\),因为一段相等的字符复制出来也是一段相等的字符,如果当前字符和前面的相等,那么可以和前面的段一起讨论,否则前面的段不能复制出来当前字符,所以\(cnt\)需要重置。

点击查看代码
void solve() {
    std::string s, t;
    std::cin >> s >> t;
    int n = s.size(), m = t.size();
    std::ranges::reverse(s);
    std::ranges::reverse(t);
    int i = 0, j = 0;
    int cnt = 0;
    while (i < n || j < m) {
    	if (i == n || s[i] != t[j]) {
    		if (i && cnt && t[j] == s[i - 1]) {
    			cnt -= 1;
    			++ j;
    		} else {
    			std::cout << "NO\n";
    			return;
    		}
    	} else {
    		if (j && t[j] != t[j - 1]) {
    			cnt = 1;
    		} else {
    			cnt += 1;
    		}
    		++ i, ++ j;
    	}
    }

    if (i != n || j != m) {
    	std::cout << "NO\n";
    	return;
    }

    std::cout << "YES\n";
}

E. Boneca Ambalabu

题意:给你一个数组,选择一个数,使得它和数组所有数的异或和的和最大。

求出二进制下每一位有多少数是\(1\)和多少数是\(0\)。然后可以枚举每个数,看在每一位下有多少数可以对它产生贡献,也就是这一位和它异或是\(1\)

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    std::array<int, 30> cnt{};
    for (int i = 0; i < n; ++ i) {
    	for (int j = 0; j < 30; ++ j) {
    		cnt[j] += a[i] >> j & 1;
    	}
    }

    i64 ans = 0;
    for (int i = 0; i < n; ++ i) {
    	i64 sum = 0;
    	for (int j = 0; j < 30; ++ j) {
    		if (a[i] >> j & 1) {
    			sum += (i64)(n - cnt[j]) * (1 << j);
    		} else {
    			sum += (i64)cnt[j] * (1 << j);
    		}
    	}
    	ans = std::max(ans, sum);
    }

    std::cout << ans << "\n";
}

F. Trulimero Trulicina

题意:给你\(n, m, k\),保证\(k\)\(n\times m\)的因子。你要构造一个\(n\times m\)的矩阵,使得\([1, k]\)的数都恰好出现\(\frac{n\times m}{k}\)次,且没有两个相邻的数相等。

如果\(m\)\(k\)的倍数,考虑第一行一直放\(1 ... k, 1 .. k..\)。然后后面每一行等于上一行左移一位就行。
否则考虑从左往右,从上到下,直接从\([1, k]\)的数依次放置,到了\(k\)就回到\(1\)。可以发现这样是合法的。因为一行相邻的两个数肯定不相等。如果一列相邻的两个数相等,说明\(m\)\(k\)的倍数,这个我们考虑过了。

点击查看代码
void solve() {
    int n, m, k;
    std::cin >> n >> m >> k;
    std::vector a(n, std::vector<int>(m));
    for (int i = 0, x = 0; i < n; ++ i) {
    	for (int j = 0; j < m; ++ j) {
    		a[i][j] = x;
    		x = (x + 1) % k;
    	}
    }

    if (m % k == 0) {
    	for (int i = 1; i < n; ++ i) {
    		for (int j = 0; j < m; ++ j) {
    			a[i][j] = a[i - 1][(j + 1) % m];
    		}
    	}
    }

    for (int i = 0; i < n; ++ i) {
    	for (int j = 0; j < m; ++ j) {
    		std::cout << a[i][j] + 1 << " \n"[j == m - 1];
    	}
    }
}

G. Chimpanzini Bananini

题意:一个数组,三种操作:

  1. 数组循环右移。
  2. 翻转数组。
  3. \(k\)添加到数组末尾。

设每次操作后长度为\(n\),求\(\sum_{i=1}^{n} a_i \times i\)

我们设计一个数据结构,用双端队列维护一个数组,记\(ans\)\(\sum_{i=1}^{n} a_i \times i\)\(sum\)\(\sum_{i=1}^{n} a_i\)。然后维护四个操作:

  1. 往后添加一个数\(k\),那么\(ans += (n + 1) \times k, sum += k\),然后把\(k\)添加到双端队列的后面。
  2. 往前添加一个数\(k\),那么\(ans += sum + k, sum += k\),然后把\(k\)添加到队列的前面。
  3. 循环右移,这个操作发现是\(ans += (\sum_{i=1}^{n-1} a_i) - (n - 1) \times a_n\),那么使得\(ans += sum - n \times a_n\),然后把最后一个数移动前面。
  4. 循环左移,同理得可以这样操作:\(ans += n \times a_1 - sum\),然后把第一个数移到后面。

那么我们建立两个这样的数据结构,一个用来维护当前数组,一个用来维护翻转后的数组。那么当前数组用到操作\(1, 3\),翻转数组进行对应的相反操作进行。如果是第二个操作,我们把当前数组标记为翻转数组,翻转数组标记为当前数组,这样就相当于实现了翻转操作。

点击查看代码
struct Node {
	std::deque<int> q;
	i64 ans, sum;
	Node() {
		ans = sum = 0;
	}

	void clear() {
		ans = sum = 0;
		while (q.size()) {
			q.pop_back();
		}
	}

	void push_back(int k) {
		q.push_back(k);
		ans += (i64)q.size() * k;
		sum += k;
	}

	void push_front(int k) {
		ans += sum;
		q.push_front(k);
		ans += k;
		sum += k;
	}

	void shift_right() {
		ans += sum;
		int x = q.back();
		ans -= (i64)q.size() * x;
		q.pop_back();
		q.push_front(x);
	}

	void shift_left() {
		ans -= sum;
		int x = q.front();
		ans += (i64)q.size() * x;
		q.pop_front();
		q.push_back(x);
	}
}a[2];

void solve() {
    int q;
    std::cin >> q;
    a[0].clear(); a[1].clear();
    int u = 0;
    while (q -- ) {
    	int op;
    	std::cin >> op;
    	if (op == 1) {
    		a[u].shift_right();
    		a[u ^ 1].shift_left();
    	} else if (op == 2) {
    		u ^= 1;
    	} else {
    		int k;
    		std::cin >> k;
    		a[u].push_back(k);
    		a[u ^ 1].push_front(k);
    	}

    	std::cout << a[u].ans << "\n";
    }
}

H. La Vaca Saturno Saturnita

题意:给你一个数组,每次给出三个数\(k, l, r\),表示\(k\)\(l\)遍历到\(r\),每次把\(a_i\)这个因子除尽,得到\(k'\)\(ans\)为所有\(k'\)的和。求出\(ans\)

这个操作有点像求质因子,然后因为\(a_i\)可能包含多个质因子,所以依次操作中\(k\)会除\(a_i\)的情况不超过其质因子个。然后计算一下得知,\(1e5\)范围的数最多有\(10\)个不同质因子,也就是说,最多在\([l, r]\)\(10\)个位置上会影响\(k\)的值。那么我们可以把\([1, 10^5]\)的数的因子都取出来。记录其在\(a\)中出现的位置,然后每次找比当前位置后面最近的因子的位置,设当前位置为\(i\),下一个因子位置为\(j\),那么有\((j - i + 1) \times k\)的贡献,然后\(i = j + 1\)。就可以求出答案。

点击查看代码
std::vector<int> factor[100010];

void init(int n) {
	for (int i = 2; i <= n; ++ i) {
		for (int j = i; j <= n; j += i) {
			factor[j].push_back(i);
		}
	}
}

void solve() {
    int n, q;
    std::cin >> n >> q;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    std::map<int, std::vector<int>> pos;
    for (int i = 0; i < n; ++ i) {
    	pos[a[i]].push_back(i);
    }

    while (q -- ) {
    	int k, l, r;
    	std::cin >> k >> l >> r;
    	-- l, -- r;
    	while (k % a[l] == 0) {
    		k /= a[l];
    	}
    	i64 ans = 0;
    	for (int i = l; i <= r;) {
    		int p = r + 1;
    		for (auto & x : factor[k]) {
    			if (pos.count(x) && pos[x].back() >= i) {
    				p = std::min(p, *std::ranges::lower_bound(pos[x], i));
    			}
    		}

    		ans += (i64)(p - i) * k;
    		if (p != r + 1) {
    			while (k % a[p] == 0) {
    				k /= a[p];
    			}
    			i = p;
    		} else {
    			break;
    		}
    	}

    	std::cout << ans << "\n";
    }
}


int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int T = 1; 
	init(1e5);
	std::cin >> T;
	//scanf("%d", &T);
	while (T -- ) {
		solve();
	} 
	return 0;
}
posted @ 2025-04-14 01:53  maburb  阅读(316)  评论(0)    收藏  举报