Codeforces Round #685 (Div. 2) 11/21

本文详细解析了四道算法竞赛题目,涉及数论、字符串处理、位操作和博弈论。针对每道题目,给出了高效解题思路,包括特殊情况的处理、奇偶性分析、子序列查找和字符计数。通过实例演示了如何运用编程技巧解决复杂问题,为参赛者提供了宝贵的实战经验。
摘要由CSDN通过智能技术生成

比赛链接

T1

中文题意:
T组输入,每次输入一个整数n,你每次可以把n除以一个它的因子,或者减1,问把n变成1的最小步骤是几次。
n ≤ 1 0 9 n\leq10^9 n109

分类讨论,先特判1,2,3三个特殊的数,再看后面的数发现我们可以分奇偶讨论了。偶数我们可以把它变成2,再减去1就是1。奇数我们一定要先减去1变成偶数在变成2再变成1。因为任何一个大于3的奇数都不可能一步变成2或者1。

#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define all(__vv__) (__vv__).begin(), (__vv__).end()
#define endl "\n"
#define pai pair<int, int>
#define ms(__x__,__val__) memset(__x__, __val__, sizeof(__x__))
#define rep(i, sta, en) for(int i=sta; i<=en; ++i)
#define repp(i, sta, en) for(int i=sta; i>=en; --i)
typedef long long ll; typedef unsigned long long ull; typedef long double ld;
inline ll read() { ll s = 0, w = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') w = -1; for (; isdigit(ch); ch = getchar())	s = (s << 1) + (s << 3) + (ch ^ 48); return s * w; }
inline void print(ll x, int op = 10) { if (!x) { putchar('0'); if (op)	putchar(op); return; }	char F[40]; ll tmp = x > 0 ? x : -x;	if (x < 0)putchar('-');	int cnt = 0;	while (tmp > 0) { F[cnt++] = tmp % 10 + '0';		tmp /= 10; }	while (cnt > 0)putchar(F[--cnt]);	if (op)	putchar(op); }
inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
ll qpow(ll a, ll b) { ll ans = 1;	while (b) { if (b & 1)	ans *= a;		b >>= 1;		a *= a; }	return ans; }	ll qpow(ll a, ll b, ll mod) { ll ans = 1; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; }
const int dir[][2] = { {0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1} };
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int N = 1e6 + 7;

ll n, m;
ll a[N];
void solve() {
	n = read();
	if (n == 1)	print(0);
	else if (n == 2)	print(1);
	else if (n == 3)	print(2);
	else if (n & 1)	print(3);
	else print(2);
}

int main() {
	//int T = read();	while (T--)
	solve();
	return 0;
}

T2

中文题意:
T组输入,给出长度为n的01串,给出m次查询,每次查询给出l,r方便代表现在截取的子串。询问这个原来的字符串中是不是还有子序列等于这个截取的子串,并且还要保证子序列不能连续,也不能等于之前子串取出来的位置。有解输出YES,没解输出NO。
1 ≤ , T , n , m ≤ 100 1\leq,T,n,m\leq100 1,T,n,m100

数据范围比较小,支持暴力查找,显然我们只判断l前面有没有和s[l]相同的字符或者r后面有没有和s[r]相同的字符就可以判断是否有解。

const int N = 1e6 + 7;
 
int n, m;
int a[N];
char s[105];
 
bool check1(int x) {
	for (int i = 1; i < x; ++i)
		if (s[i] == s[x])	return true;
	return false;
}
 
bool check2(int x) {
	for (int i = x + 1; i <= n; ++i)
		if (s[i] == s[x])	return true;
	return false;
}
 
void solve() {
	n = read(), m = read();
	scanf("%s", s + 1);
	while (m--) {
		int l = read(), r = read();
		if (check1(l) or check2(r))	puts("YES");
		else puts("NO");
	}
}

T3

中文题意:
T组输入,每次输入长度为n的只包含小写字母的字符串s,以及你可以操作的序列长度m。现在你可以进行如下两种操作。

  1. 操作一:交换任意两个相邻的字符
  2. 操作二:选择长度为m的子串,把子串中全部字符变成+1表示,例如aab变成bbc

你可以执行上面的操作无数次询问能不能把s变成后面输入的字符串t。
n ≤ 1 0 6 , m ≤ n n\leq10^6,m\leq n n106,mn

首先观察操作1,我们发现可以随意交换的话,那么他本身给我们的起始字符串长什么样,顺序就变得无关紧要了。我们只需要关注每个字符之前出现的次数就行了,只要知道这样的次数我们就可以知道能不能变成t串。具体做法就是使用计数器统计当前字母在s出现了几次,在t出现了几次,做个假设如果字符a在s出现的次数比t中出现的次数还要更小的话,是不是就说明了无解,如果a出现次数更大,说明我们需要选择长度为m的段把它弄成和t一样多,做个减法,判断能不能被m整除就行了,最后多出来的a就全部变成b了。依次模拟过去就可以找到答案。

const int N = 1e6 + 7;
 
int n, m;
char s[N], ss[N];
int a[30], b[30];
 
void solve() {
	n = read(), m = read();
	ms(a, 0);
	ms(b, 0);
	scanf("%s", s + 1);
	scanf("%s", ss + 1);
	for (int i = 1; i <= n; ++i)
		++a[s[i] - 'a'];
	for (int i = 1; i <= n; ++i)
		++b[ss[i] - 'a'];
	bool flag = 0;
	for (int i = 0; i < 26; ++i) {
		if (a[i] < b[i] or (a[i] -= b[i]) % m)
			flag = 1;
		a[i + 1] += a[i];
	}
	if (flag)	puts("No");
	else puts("Yes");
}

T4

中文题意:
T组输入,每组输入两个整数d,k。假设起点在(0,0)处,每次轮到一名玩家操作时,就要把x增加k或者把y增加k,可以移动的点一定是 x ∗ x + y ∗ y ≤ d ∗ d x*x+y*y\leq d*d xx+yydd的地方,谁最后无法移动谁就输掉了。
d ≤ 1 0 5 , k ≤ d d\leq10^5,k\leq d d105,kd

我们可以找到最大的一个整数z,使得在对角线y=x直线上的合法点最远处找到。也就是(zk,zk)这样的二维坐标点在圆内,如果(zk+k,zk)或者(zk,zk+k)不在圆内的话说明后手一定会赢的比赛,因为无论你先手如何位移,我后手都可以把它重新移回y=x处,直到走到(zk,zk)先手就没办法动了。那么同理如果(zk+k,zk)在圆内的话,先手一动,相当于把坐标原点平移,并且先后手互换,无论后手走去什么地方都可以走到y=x+k或者y=x-k这条直线上,保证自己最后一定获胜。

ll d, k;

bool check(ll x, ll y) {
	return x * x + y * y <= d * d;
}

void solve() {
	d = read(), k = read();
	ll x = 0, y = 0;
	while (1) {
		if (x <= y and check(x + k, y))
			x += k;
		else if (x > y and check(x, y + k))
			y += k;
		else break;
	}
	if (x == y)	puts("Utkarsh");
	else puts("Ashish");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值