Codeforces Round #553 (Div. 2)/codeforces1151

2022-11-18,

CodeForces1151

Maxim and Biology

解析:

题目大意

每次可以使原串中的一个字符\(+1/-1\),\(Z + 1\to A, A -1\to Z\),求至少修改多少次可以使 ACTG 是原串的子串

\(4\le |S|\le 50\)


思路:

直接暴力尝试每个子串。


code

#include <bits/stdc++.h>
const int N = 50 + 10;
const int INF = 0x3f3f3f3f;
using namespace std;
int n, ans;
char s[N];
signed main()
{
scanf ("%d%s", &n, s + 1); ans = INF;
for (int i = 1; i <= n - 3; i++)
{
ans = min (ans, min (abs(s[i] - 'A'), 26 - abs((s[i] - 'A'))) +
min (abs(s[i + 1] - 'C'), 26 - abs((s[i + 1] - 'C'))) +
min (abs(s[i + 2] - 'T'), 26 - abs((s[i + 2] - 'T'))) +
min (abs(s[i + 3] - 'G'), 26 - abs((s[i + 3] - 'G'))));
}
printf ("%d\n", ans);
return 0;
}

Dima and a Bad XOR

解析:

题目大意:

给你一个大小为 \(n \times m\) 的矩阵,其中只包含非负整数。你需要从矩阵的每一行中选出一个整数,使得所选整数的按位异或严格大于零。

输出 \(n\) 行,每行一个正整数表示选择选当前第几个数。


思路:

考虑异或和大于零的充要条件是某一位不为零,那么可以拆位考虑。现在问题变成了给你一个0/1矩阵,让你在每行选一个数使最终异或和为1。

根据异或的性质可以发现选 0 不会影响异或结果,所以我们只需要找出来奇数个1即可。

我们先判断所有行中有 1 的行的个数,如果为奇数那每行都选 1 即可。如果个数为偶数那就找一个既有 0 也有 1 的行强行选 0 即可。


code:

#include <bits/stdc++.h>
using namespace std;
const int N = 500 + 10;
inline int read ()
{
int x = 0, f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
return x * f;
}
int n, m;
int a[N][N];
void solve (int x, int d)
{
printf ("TAK\n");
for (int i = 1; i <= n; i++)
{
bool flag2 = false;
if (x != i)
{
for (int j = 1; j <= m; j++)
if ((a[i][j] >> d) & 1) { printf ("%d ", j); flag2 = true; break; }
}
else
{
for (int j = 1; j <= m; j++)
if (!((a[i][j] >> d) & 1)) { printf ("%d ", j); flag2 = true; break; }
}
if (!flag2) printf ("1 ");
}
}
signed main()
{
n = read (), m = read ();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) a[i][j] = read ();
for (int d = 10; d >= 0; d--)
{
int cnt = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if ((a[i][j] >> d) & 1) { cnt++; break; }
if (cnt & 1) { solve (0, d); return 0; }
else
{
for (int i = 1; i <= n; i++)
{
int cnt2 = 0;
for (int j = 1; j <= m; j++)
if ((a[i][j] >> d) & 1) cnt2++;
if (cnt2 && cnt2 < m) { solve (i, d); return 0; }
}
}
}
printf ("NIE\n");
return 0;
}

Problem for Nazar

解析:

题目大意

当\(i\)为奇数时,从集合\(\mathrm{A}\)中依次取数(取过了),当\(i\)为偶数时,从集合\(\mathrm{B}\)中向后取数。求黑板上第\(l\)个数到第\(r\)个数的和,答案对 \(10^9+7\)取余。

\(1 \le l,r \le 10^{18}\)


思路:

考虑把询问的区间按照奇数和偶数分成若干段,最多有 \(\log 10^{18}\) 个段,于是直接暴力即可。


code

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
const int INV = 500000004;
const int mods = 1e9 + 7;
typedef pair <int, int> pii;
inline int read ()
{
int x = 0, f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
return x * f;
}
int l, r;
int sum1, sum2;
int ans;
signed main()
{
l = read (), r = read ();
int sum = 0;
for (int i = 1; i <= 100; i++)
{
int lst = sum + 1;
sum += (1ll << (i - 1));
int b, e, cnt;
if (sum >= l)
{
if (i & 1)
{
b = 2 * (sum1 + 1 + ((l > lst) ? l - lst : 0)) - 1;
cnt = min (r, sum) - max (lst, l) + 1; cnt %= mods;
e = b + 2 * (cnt - 1);
ans += (b + e) % mods * cnt % mods * INV % mods;
}
else
{
b = 2 * (sum2 + 1 + ((l > lst) ? l - lst : 0));
cnt = min (r, sum) - max (lst, l) + 1; cnt %= mods;
e = b + 2 * (cnt - 1);
ans += (b + e) % mods * cnt % mods * INV % mods;
}
ans %= mods;
}
if (sum >= r || sum > 1e18) break;
if (i & 1) sum1 += (1ll << (i - 1));
else sum2 += (1ll << (i - 1));
}
printf ("%lld\n", ans);
return 0;
}

Stas and the Queue at the Buffet

解析:

题目大意:

共有 \(n\) 个人,第 \(i\) 个人有属性值 \(a_i,b_i\),现在要给这 \(n\) 个人重排顺序,设第 \(i\) 个人站在位置 \(j\),则他的不满意度是 \(a_i \times (j-1) + b_i \times (n-j)\)

最小化所有人的不满意度之和,求这个最小值


思路:

我们变一下式子:

\[\sum_{i=1}^{n}a_i\times (i-1)+b_i\times (n-i)\\
\sum_{i=1}^{n}a_i\times i-a_i-b_i\times i\\
n\times \sum_{i=1}^n b_i+\sum_{i=1}^na_i+\sum_{i=1}^{n}(a_i-b_i)\times i\\
\]

问题变为 \(\sum_{i=1}^n(a_i-b_i)\times i\) 的最小值,我们按照 \(a_i-b_i\) 从大到小排序即可。


code:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
typedef pair <int, int> pii;
inline int read ()
{
int x = 0, f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
return x * f;
}
int n;
struct node {
int x, y, z;
bool operator < (const node &A) const {
return z > A.z;
}
} a[N];
signed main()
{
n = read ();
for (int i = 1; i <= n; i++)
a[i].x = read (), a[i].y = read (), a[i].z = a[i].x - a[i].y;
sort (a + 1, a + n + 1);
int ans = 0;
for (int i = 1; i <= n; i++) ans += a[i].x * (i - 1) + a[i].y * (n - i);
printf ("%lld\n", ans);
return 0;
}

Number of Components

解析:

题目大意:

有一条 \(n(1 \leq n \leq 10^5)\) 个节点的链,编号相邻节点有边,每个点一个权值 \(a_i(1 \leq a_i \leq n)\),\(f(l,r)\)定义为权值在\([l,r]\) 的点中的连通块数量求 \(\sum_{l=1}^{n}\sum_{r=l}^{n} f(l,r)\) 。


思路:

考虑套路:连通块个数=所有权值在 \([l,r]\) 内的点的个数 \(-\) 连接他们的边的个数,这个是可以分开计算的。

我们考虑每个点对答案的贡献,不难发现对于 \(l\leq a_i \and a_i \leq r\) 的所有区间这个点都贡献了一次,答案为 \(a_i\times (n-a_i+1)\) ,这部分可以线性解决。

边权同理,我们把 \(a_i\rightarrow a_{i+1}\) 的边当作是一个点,类似二维数点,考虑 \(\forall l, r|l\in[1,\min(a_i,a_{i+1})],r\in[\max(a_i,a_{i+1}), n]\) 的 \((l,r)\) 都会对答案造成 \(-1\) 的贡献,一共贡献了 \(\min(a_i,a_{i+1})\times(n-\max(a_i,a_{i+1}+1))\) 次。

总联通块个数做差即可,时间复杂度线性。


code:

#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef pair <int, int> pii;
inline int read ( )
{
int x = 0, f = 1;
char ch = getchar ( );
while (ch < '0' || ch > '9') {if (ch == '-') f = - 1; ch = getchar ( );}
while (ch >= '0' && ch <='9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar ( );}
return x * f;
}
int n, ans;
signed main()
{
n = read (); int lst, now;
for (int i = 1; i <= n; i++)
{
now = read ();
ans += (now * (n - now + 1));
if (i > 1) ans -= (min (lst, now) * (n - max (lst, now) + 1));
lst = now;
}
printf ("%lld\n", ans);
return 0;
}

Sonya and Informatics

解析:

题目大意:

给一个长度为 \(n\) \((n\leq 100)\) 的 \(0/1\) 串,进行 \(k\) \((k \leq 10^9)\) 次操作,每次操作随机选择两个位置 \(i,j\) \((1 \leq i < j \leq n)\),交换 \(i,j\) 上的数,求 \(k\) 次操作后,该 \(0/1\) 串变成非降序列的概率,答案对 \(10^9+7\) 取模。


思路:

考虑非降要求最终序列一定是前面是 0 后面是 1,假设 0 有 \(cnt\) 个,那么设 \(dp_{i,j}\) 表示交换了 \(i\) 次后前 \(cnt\) 个位置有 \(j\) 个 \(0\),那么有转移:

\[dp_{i+1,j+1}=dp_{i+1,j+1}+dp_{i,j}\times \frac{(cnt-j)^2}{\frac{n\times (n-1)}{2}}
\]

其中 \(\frac{n\times (n-1)}{2}\) 可以预处理逆元,\((cnt-j)^2\) 表示恰好选到前 \(cnt\) 个位置中的 1 的和后 \(n-cnt\) 中 0 的概率。

同时有转移:

\[dp_{i+1,j-1}=dp_{i+1,j-1}+dp_{i,j}\times \frac{j\times (n-cnt-(cnt-j))}{\frac{n\times (n-1)}{2}}
\]

\(j\times (n-cnt-(cnt-j))\) 表示恰好选到前 \(cnt\) 个位置中的 0 的和后 \(n-cnt\) 中 1 的概率。

剩下的转移到 \(dp_{i+1,j}\) 即可。

我们发现 \(n\leq 100\) 而 \(k\leq 10^9\) ,这显然可以矩快优化dp,我们设计如下矩阵:

\[A=\begin{bmatrix}
P(3,0)&P(1,0)&0&0&0&\cdots &0\\
P(2,1)&P(3,1)&P(1,1)&0&0&\cdots &0\\
0&P(2,2)&P(3,2)&P(1,2)&0&\cdots &0\\
0&0&P(2,3)&P(3,3)&P(1,3)&\cdots &0\\
\vdots & \vdots & \vdots &\vdots&\vdots & \ddots & \vdots \\
0&0&0&0&0&\cdots &P(3,cnt)\\
\end{bmatrix}
\]

其中 \(P(i,j)\) 表示第 \(i\) 类转移,转移前的前 \(cnt\) 个中有 \(j\) 个 0,那么有:

\[\begin{bmatrix}
dp_{i,0}&dp_{i,1}&dp_{i,2}&\cdots &dp_{i,cnt}
\end{bmatrix}
\times
A
=
\begin{bmatrix}
dp_{i+1,0}&dp_{i+1,1}&dp_{i+1,2}&\cdots &dp_{i+1,cnt}
\end{bmatrix}
\]

考虑设初始状态 \(dp_{0,x}=1\),其中 \(x\) 表示前 \(cnt\) 个中的 0 的数量,那么答案为:

\[\begin{bmatrix}
dp_{0,0}&dp_{0,1}&dp_{0,2}&\cdots &dp_{0,cnt}
\end{bmatrix}
\times
A^k
=
\begin{bmatrix}
dp_{m,0}&dp_{m,1}&dp_{m,2}&\cdots &dp_{m,cnt}
\end{bmatrix}
\]

则答案为 \(dp_{m,cnt}\)。

时间复杂度 \(\mathcal O(n^3\log k)\)。


code:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100 + 10;
const int mods = 1e9 + 7;
typedef pair <int, int> pii;
inline int read ()
{
int x = 0, f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar (); }
while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar (); }
return x * f;
}
int n, m;
int a[N], cnt;
struct Matrix {
int h, r;
int m[N][N];
void init () { for (int i = 1; i <= h; i++) m[i][i] = 1; }
}ans;
Matrix operator * (const Matrix x, const Matrix y)
{
Matrix res;
res.h = x.h; res.r = y.r;
memset (res.m, 0, sizeof (res.m));
for (int i = 1; i <= x.h; i++)
for (int j = 1; j <= y.r; j++)
for (int k = 1; k <= x.r; k++)
(res.m[i][j] += ((x.m[i][k] * y.m[k][j]) % mods)) %= mods;
return res;
}
Matrix qpow (Matrix a, int p)
{
Matrix res;
res.h = res.r = a.h;
res.init ();
while (p) { if (p & 1) res = res * a; a = a * a; p >>= 1; }
return res;
}
int qpow2 (int a, int p)
{
int res = 1;
while (p) { if (p & 1) res = (res * a) % mods; a = (a * a) % mods; p >>= 1; }
return res;
}
signed main()
{
n = read (), m = read ();
for (int i = 1; i <= n; i++) a[i] = read (), cnt += !(a[i]);
ans.h = 1, ans.r = cnt + 1;
int cnt2 = 0;
for (int i = 1; i <= cnt; i++) cnt2 += !(a[i]);
ans.m[1][cnt2 + 1] = 1;
Matrix e;
e.h = e.r = cnt + 1;
int inv = qpow2 (n * (n - 1) / 2, mods - 2);
for (int i = 0; i <= cnt; i++)
{
int x, y, z;
x = ((cnt - i) * (cnt - i)) % mods;
y = (i * (n - cnt - cnt + i)) % mods;
z = (n * (n - 1) / 2 - x - y) % mods;
(x *= inv) %= mods; (y *= inv) %= mods; (z *= inv) %= mods;
e.m[i + 1][i + 1] = z; e.m[i + 1][i + 2] = x; e.m[i + 1][i] = y;
}
Matrix q = qpow (e, m);
ans = ans * q;
printf ("%lld\n", ans.m[1][cnt + 1]);
return 0;
}

Codeforces Round #553 (Div. 2)/codeforces1151的相关教程结束。

《Codeforces Round #553 (Div. 2)/codeforces1151.doc》

下载本文的Word格式文档,以方便收藏与打印。