T1
中文题意:
T组输入,每次输入一个整数n,你每次可以把n除以一个它的因子,或者减1,问把n变成1的最小步骤是几次。
n
≤
1
0
9
n\leq10^9
n≤109
分类讨论,先特判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,m≤100
数据范围比较小,支持暴力查找,显然我们只判断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。现在你可以进行如下两种操作。
- 操作一:交换任意两个相邻的字符
- 操作二:选择长度为m的子串,把子串中全部字符变成+1表示,例如aab变成bbc
你可以执行上面的操作无数次询问能不能把s变成后面输入的字符串t。
n
≤
1
0
6
,
m
≤
n
n\leq10^6,m\leq n
n≤106,m≤n。
首先观察操作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
x∗x+y∗y≤d∗d的地方,谁最后无法移动谁就输掉了。
d
≤
1
0
5
,
k
≤
d
d\leq10^5,k\leq d
d≤105,k≤d。
我们可以找到最大的一个整数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");
}