A. Avoid Trygub
输入长度为n的字符串s,你可以对字符串进行顺序的调换,保证"trygub"不是新串的子串。
n
≤
200
n\leq200
n≤200
输出字典序最小的那个即可。
B. Balls of Steel
给出n个小球的坐标
x
i
,
y
i
x_i,y_i
xi,yi,以及静电的影响范围k。你可以选择任意小球带点,如果一个球带电他就会吸引坐标相减绝对值小于等于k的其余小球。问你要使得全部小球都聚拢在一个坐标,你需要最少让几个球带电,如果无法聚拢输出-1。
2
≤
n
≤
100
,
k
≤
1
0
6
,
1
≤
x
i
,
y
i
≤
1
0
5
2\leq n\leq100,k\leq10^6,1\leq x_i,y_i\leq10^5
2≤n≤100,k≤106,1≤xi,yi≤105
如果我们下意识思考,如果我们选定一个点他不能吸到全部的点,那么我们就可以把可以被他吸的点带电,先把别的小球吸过来,再一起吸走。但是再仔细思考,如果一个球可以吸到全部另外一个球无法吸到的球,并且这个球距离也在k之内,那么我为什么要花两次带电机会使得全部聚拢呢,直接使得那个可以吸全部球的带电就行了,如果没有这样的球,使得其他球距离都小于k。就说明永远是无解的。
#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 = 100 + 7;
ll n, m;
pai a[N];
#define x first
#define y second
int check(pai A, pai B) {
return abs(A.x - B.x) + abs(A.y - B.y);
}
void solve() {
n = read(), m = read();
rep(i, 1, n) a[i].x = read(), a[i].y = read();
rep(i, 1, n) {
bool flag = 1;
rep(j, 1, n) {
if (check(a[i], a[j]) > m)
flag = 0;
}
if (flag) { print(1); return; }
}
print(-1);
}
int main() {
int T = read(); while (T--)
solve();
return 0;
}
C1. Errich-Tac-Toe (Easy Version)
给出 n ∗ n n*n n∗n棋盘,棋盘中有X一种棋子,也还有.(点)代表这个地方没有棋子。现在横着或者竖着三个相同的棋子就会有玩家赢得比赛。现在要你修改m个地方,使得当前格局下没有玩家获胜。m应该小于 ⌊ k 3 ⌋ \lfloor\frac{k}{3}\rfloor ⌊3k⌋,k是起始的棋子数目。
我们把地图下标从0开始编号,使用 ( i + j ) % 3 (i+j)\%3 (i+j)%3对全部的格子进行分类,你会发现我们按照斜线分成三种。我们选择包含棋子最少的那一种格子把他全部变成O,就可以使得X一定无法获得胜利,并且保证了m的要求。
const int N = 300 + 7;
ll n, m;
char s[N][N];
int cnt[3];
void solve() {
n = read();
ms(cnt, 0);
rep(i, 0, n - 1) {
scanf("%s", s[i]);
rep(j, 0, n - 1)
if (s[i][j] == 'X') ++cnt[(i + j) % 3];
}
int pos = min_element(cnt, cnt + 3) - cnt;
rep(i, 0, n - 1) {
rep(j, 0, n - 1) {
int tmp = (i + j) % 3;
if (s[i][j] == 'X' and tmp == pos)
putchar('O');
else putchar(s[i][j]);
}
puts("");
}
}
C2. Errich-Tac-Toe (Hard Version)
困难版本在起始的棋盘会存在X和O两种棋子。
我们同样把棋盘按照位置分隔开,但是我们还要分别统计这个斜线中X的个数以及O的个数。那么同理我们三种样式中,一定有两种是X一种是O或者两种是O一种是X。并且我们发现这样枚举之后任选一个行成为X并且任选另外一个和它不同的下标的成为O,这样就可以保证没玩家胜利。但是如何保证一定满足m的大小要求呢。显然我把全部不同下标的计数器累加起来会得到
2
∗
k
2*k
2∗k个格子,相当于省略了k个完全一样的。那么
2
k
6
\frac{2k}{6}
62k是满足题目要求的,只需要找到满足要求的那个解即可。
const int N = 300 + 7;
ll n, m;
char s[N][N];
int cnt[3][2];
void solve() {
n = read();
ms(cnt, 0);
m = 0;
rep(i, 0, n - 1) {
scanf("%s", s[i]);
rep(j, 0, n - 1) {
if (s[i][j] == '.') continue;
int tmp = (i + j) % 3;
if (s[i][j] == 'X') ++cnt[tmp][0];
else ++cnt[tmp][1];
++m;
}
}
m /= 3;
rep(i, 0, 2) {
rep(j, 0, 2) {
if (i == j) continue;
if (cnt[i][0] + cnt[j][1] > m) continue;
rep(x, 0, n - 1) {
rep(y, 0, n - 1) {
int tmp = (x + y) % 3;
if (tmp == i and s[x][y] == 'X') s[x][y] = 'O';
else if (tmp == j and s[x][y] == 'O') s[x][y] = 'X';
}
puts(s[x]);
}
return;
}
}
}
D. Rating Compression
给出长度为n的序列,判断选出长度为k的子数组并且求出你找到的子数组最小值,依次从后放入新数组中,如果新数组中构成了一个长度为
n
−
k
+
1
n-k+1
n−k+1的全排列说明长度为k的时候答案是1,否则答案是0,要你输出一个长度是n的答案字符串。
n
≤
3
∗
1
0
5
n\leq3*10^5
n≤3∗105
首先先看特殊的k。如果k=1,需要判断起始给出的序列是不是全排列。
如果k=n,需要判断序列中是不是存在1。
如果k=n-1,我们首先要判断
a
1
a_1
a1或者
a
n
a_n
an是不是1,假设
a
1
a_1
a1是1,说明我们在[2,n]之间最小值存在2,并且只有一个2。如果
a
n
a_n
an是1,说明我们在[1,n-1]之间存在一个2。
如果k=n-2,我们要先从左边或者右边依次往中间一共走两步并且保证1和2只能出现1次,并且一定要在开始就跨过去这两个点,并且使得中间没有走过的地方存在3。
那么到这里就有点看出规律了。首先要特判k=1,n,接来下就把游标放在左右端点。l=1,r=n。我们依次放入1到n的全排列去判断,假设k=n-2的时候判断失败了,说明k=n-3的时候一定也无解,因为你要把序列分的更碎,这个你判断失败的地方还是要选择长度为2的去生成,仍然无解。只有当k=1的时候这个要特判一下。
const int N = 3e5 + 7;
ll n, m;
int a[N];
char s[N];
void solve() {
n = read();
unordered_map<int, int> mp;
ms(s, 0);
rep(i, 1, n) {
a[i] = read();
++mp[a[i]];
s[i] = '0';
}
if (mp[1] != 0) s[n] = '1';
else {
puts(s + 1);
return;
}
bool flag = 1;
rep(i, 1, n)
if (mp[i] != 1) { flag = 0; break; }
if (flag) s[1] = '1';
int l = 1, r = n;
rep(i, 1, n) {
s[n - i + 1] = '1';
if (mp[i] == 1 and mp[i + 1] != 0 and (a[l] == i or a[r] == i)) {
if (a[l] == i) ++l;
else if (a[r] == i) --r;
--mp[i];
}
else
break;
}
puts(s + 1);
}
E. Capitalism
给出n个点m条边构成的无向图,点权信息不给出需要你进行推理。后面给出边的信息中会存在三个参数
u
,
v
,
d
u,v,d
u,v,d。当d=1时,说明v的点权比u大一,如果d=0,说明v的点权可能比u大一,也可能u的点权比v的点权大一。现在要你给出这个图是不是相邻节点点权相差1,如果可以构造出这样的点权请输出点权相差最大的两个点之间差值是多少。否则输出一个NO。
n
≤
200
,
m
≤
2000
n\leq200,m\leq2000
n≤200,m≤2000
差分约束,我们知道我们相邻的点权一定只相差1,那么如果我们按照点权的奇偶建图会发现我们拿到的可行解一定是一个二分图,那么题目给出的边权信息,我们跑一边,首先判断是不是二分图,如果不是二分图说明无解。
接下来就要看题目给的
d
=
1
,
a
v
−
a
u
=
1
d=1,a_v-a_u=1
d=1,av−au=1,我们把这个拆分会拿到
a
v
−
a
u
≤
1
a
v
−
a
u
≥
1
→
a
u
−
a
v
≤
−
1
a_v-a_u\leq1\\a_v-a_u\geq1\to a_u-a_v\leq-1
av−au≤1av−au≥1→au−av≤−1
因为差分约束系统中要使得不等式符号方向相同,我们做个简单的变形即可。
还要就是
d
=
0
,
∣
a
v
−
a
u
∣
=
1
d=0,|a_v-a_u|=1
d=0,∣av−au∣=1,我们分情况讨论。
- 如果 a v − a u = 1 a_v-a_u=1 av−au=1和上面情况相同
- 或者 a u − a v = 1 a_u-a_v=1 au−av=1,我们可以解得 a u − a v ≤ 1 a v − a u ≤ − 1 a_u-a_v\leq1\\a_v-a_u\leq-1 au−av≤1av−au≤−1进行合并之后会发现我们两点之间得要求就是 a u − a v ≤ 1 a v − a u ≤ 1 a_u-a_v\leq1\\a_v-a_u\leq1 au−av≤1av−au≤1
紧接着跑Floyd就可以拿到最短路,再根据差分约束系统有解条件,不能存在负环。拿到最大值就行了。
const int N = 200 + 7;
ll n, m;
int w[N][N];
int fa[N << 1];
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
void merge(int u, int v) {
int fu = find(u), fv = find(v);
fa[fv] = fu;
}
void solve() {
n = read(), m = read();
ms(w, 0x3f);
rep(i, 1, n << 1) fa[i] = i;
rep(i, 1, n) w[i][i] = 0;
while (m--) {
int u = read(), v = read(), d = read();
if (d) {
w[u][v] = 1;
w[v][u] = -1;
}
else {
w[u][v] = 1;
w[v][u] = 1;
}
merge(u, v + n);
merge(v, u + n);
}
rep(i, 1, n) {
if (fa[i] == fa[i + n]) {
puts("NO");
return;
}
}
rep(k, 1, n)
rep(i, 1, n)
rep(j, 1, n)
w[i][j] = min(w[i][j], w[i][k] + w[k][j]);
int st, maxi = -INF;
rep(i, 1, n) {
if (w[i][i] < 0) {
puts("NO");
return;
}
rep(j, 1, n) {
if (w[i][j] > maxi)
maxi = w[i][j],
st = i;
}
}
puts("YES");
print(maxi);
rep(j, 1, n)
print(w[st][j], " \n"[j == n]);
}
F. The Struggling Contestant
给你长度为n的序列,要你重新排序之后使得新的序列中不存在相邻两个数相同。输出新排列中
∑
i
=
1
j
=
i
+
1
∣
i
−
j
∣
>
1
?
1
:
0
\sum\limits^{j=i+1}_{i=1}|i-j|>1?1:0
i=1∑j=i+1∣i−j∣>1?1:0,既排列后新的序列中相邻两个位置之前的原下标差距大于1的和是多少。如果无法使得新序列中不存在相邻数输出-1。
n
≤
1
0
5
n\leq10^5
n≤105
先判断输出-1的情况,那就是插空法,不足以完全分开。判断下是不是那个数出现次数大于了 n + 1 2 \frac{n+1}{2} 2n+1。首先我们看13441,我们一定是把41一起放到最前面去形成14134,答案是1。那么通过这个我们可以发现,相同的数之间一定要插入一个数或者一段数,并且如果可以插入一段数不会选择这一段数里面的单独一个数。那么我们就可以使用分段把这个待处理的序列拆分掉。使用一个 f [ n ] f[n] f[n]代表n做为分隔端点的次数,如果 a i = a i − 1 a_i=a_{i-1} ai=ai−1说明 a i a_i ai做为端点出现次数要+2,还有天然的端点 a 1 a_1 a1和 a n a_n an这两个做为端点出现次数也要+1。
那么我们的答案就是
k
+
max
(
0
,
(
max
i
=
1
n
f
i
)
−
k
−
2
)
k+\max(0,(\max\limits_{i=1}^n f_i)-k-2)
k+max(0,(i=1maxnfi)−k−2)。
k是
a
i
=
a
i
−
1
a_i=a_{i-1}
ai=ai−1的计数器,存在3对相同的数,一定需要3为最小值。因为你要从别的地方插入一个去中间,那么无论是把左边端点还是右边端点插入,都一定会存在1的贡献,那么k对数就是合理划分就是k的贡献。那么什么时候会需要额外加一些答案,那就是一个数做为端点最多的那个数,即使把他放在两端用掉两个天然的端点并且使用其余的端点无法拆分之后,你就需要从这一段数中间拿出一个数出去接着分。这样的时候答案贡献就要额外+1。那么我们找到最多需要几个这样的额外答案即可。
const int N = 1e5 + 7;
ll n, m;
int a[N], f[N];
void solve() {
n = read();
ms(a, 0);
ms(f, 0);
unordered_map<int, int> mp;
m = 0;
int maxi = 0;
rep(i, 1, n) {
a[i] = read();
++mp[a[i]];
maxi = max(maxi, mp[a[i]]);
if (a[i] == a[i - 1])
f[a[i]] += 2, ++m;
}
if (maxi > (n + 1) / 2) print(-1);
else {
++f[a[1]]; ++f[a[n]];
ll tmp = max(0ll, *max_element(f + 1, f + n + 1) - m - 2);
m += tmp;
print(m);
}
}