A. Copy-paste
题意:
给你一个数组和一个限定值
k
k
k,你可以进行法术使得:
当
1
≤
i
,
j
≤
n
1≤i,j≤n
1≤i,j≤n
a
n
d
and
and
i
≠
j
i ≠ j
i=j 让
a
j
:
=
a
j
+
a
i
a_j := a_j + a_i
aj:=aj+ai 。
当进行法术操作使得其中有一个元素超过限定值 k k k 时,你都将失去该法术能力,即法术无效。问你最多能使用多少次法术。
直接看最小的可以加几次。
AC代码:
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
int t;
int n, m, k;
int a[N], b[N], ans[N];
bool vis[N];
int res;
int main()
{
sd(t);
while (t--)
{
sdd(n, k);
ll ans = 0;
rep(i, 1, n)
sd(a[i]);
sort(a + 1, a + 1 + n);
int minn = a[1];
rep(i, 2, n)
{
res = (k - a[i]) / minn;
ans += res;
}
pld(ans);
}
return 0;
}
B. Two Arrays
题意:
给你一个数组
a
a
a 和不幸值
T
T
T,要求你将数组
a
a
a 分成数组
b
b
b和数组
c
c
c,使得
f
(
b
)
+
f
(
c
)
f ( b ) + f ( c )
f(b)+f(c)最小。
其中
f
(
b
)
f ( b )
f(b) 的值为数组
b
b
b 中
1
≤
i
<
j
≤
m
1 ≤ i < j ≤ m
1≤i<j≤m 使得
a
i
+
a
j
=
T
a_i+a_j=T
ai+aj=T 的数量对数。
对于等于 T / 2 T/2 T/2 的单独处理交叉放置,大于 T / 2 T/2 T/2 的放一组,小于 T / 2 T/2 T/2 的放一组。
AC代码:
const int N = 2e5 + 10;
const int mod = 998244353;
int n, m, k, T;
int a[N], ans[N];
int main()
{
int t;
sd(t);
while (t--)
{
sdd(n, T);
int cnt = 0;
rep(i, 1, n)
{
sd(a[i]);
if (T % 2 == 0 && a[i] == T / 2)
{
cnt++;
if (cnt & 1)
ans[i] = 1;
else
ans[i] = 0;
}
else if (a[i] * 2 < T)
ans[i] = 0;
else
ans[i] = 1;
}
rep(i, 1, n)
printf("%d%c", ans[i], i == n ? '\n' : ' ');
}
return 0;
}
C. k-Amazing Numbers
题意:
这道题的题意很难理解。
给你两个数 n , k n,k n,k ,求从 1 n 1~n 1 n 之间的每一个 i i i 在长度为 n n n 的数组中的,所有连续的长度为i的数组的公共最小值,如果没有重复值则输出 − 1 -1 −1。
遍历求每个相同的数之间的距离,即求出i到达几时这些数成为公共值,然后遍历所有求得的公共值,输出最小值即可。
AC代码:
const int N = 3e5 + 10;
const int mod = 998244353;
int n, m, k, T;
int a[N], ans[N];
vector<int> v[N];
int main()
{
int t;
sd(t);
while (t--)
{
sd(n);
rep(i, 0, n)
{
ans[i] = inf;
v[i].clear();
}
rep(i, 1, n)
{
sd(a[i]);
v[a[i]].pb(i);
}
rep(i, 1, n)
{
if (!v[i].empty())
{
int res = 0;
rep(j, 1, v[i].size() - 1)
res = max(res, v[i][j] - v[i][j - 1]); //相同数最大的距离
res = max(res, v[i].front());
res = max(res, n - v[i].back() + 1);
ans[res] = min(ans[res], i);
}
}
rep(i, 2, n)
ans[i] = min(ans[i], ans[i - 1]);
rep(i, 1, n)
{
if (ans[i] < inf)
printf("%d ", ans[i]);
else
printf("-1 ");
}
printf("\n");
}
return 0;
}
D. Make Them Equal
题意:
给出最大为 3 ∗ n 3*n 3∗n,所以可以考虑扫三次序列。最后一次应该是把通过一个数把全部数字变成平均数。那么前两次应该是收集到一个数字上。
先把所有值搞到
a
1
a_1
a1 上,最多需要
2
(
n
−
1
)
2(n-1)
2(n−1) 次操作:如果
a
i
%
i
=
0
a_i\%i=0
ai%i=0 一次操作就可以否则需要先用
a
1
a_1
a1去把
a
i
a_i
ai调整成
i
i
i 的倍数,然后再需要一次操作。
然后直接用
a
1
a_1
a1 把所有值搞成平均数即可最多
n
−
1
n-1
n−1次操作。
AC代码:
const int N = 2e5 + 10;
const int mod = 998244353;
int n, m, k;
int a[N];
int main()
{
int t;
sd(t);
while (t--)
{
sd(n);
ll sum = 0;
int cnt = 0;
rep(i, 1, n)
{
sd(a[i]);
sum += a[i];
if (a[i] % i != 0)
cnt++;
}
if (sum % n != 0)
{
puts("-1");
continue;
}
int ave = sum / n;
pd(2 * (n - 1) + cnt);
rep(i, 2, n)
{
if (a[i] % i == 0)
{
printf("%d 1 %d\n", i, a[i] / i); //能整除先都加到a[1]上
}
else
{
printf("1 %d %d\n", i, i - a[i] % i); //不能整除的话先从a[1]加上
printf("%d 1 %d\n", i, a[i] / i + 1); //在减去,因为并没有实际的加上所以这里需要+1操作
}
}
rep(i, 2, n)
{
printf("1 %d %d\n", i, ave);
}
}
return 0;
}
E. XOR Inverse
题意:
给一个数组 ,需要找到一个 X X X ,使数组中每个数 异或 X X X 后 ,数组中的逆序对数最少 ,如果有多个 X X X 找最小的那个,求 X X X 和逆序对数。
字典树
AC代码:
const int N = 4e6 + 10;
const int mod = 998244353;
int n, m, k;
int a[N];
int tot = 1, tire[N][3];
ll dp[40][3];
ll ans1, ans2;
vector<int> v[N];
void insert(int x, int pos)
{
int p = 0, t;
for (int i = 30; i >= 0; i--)
{
t = (x >> i) & 1;
if (tire[p][t] == 0)
tire[p][t] = tot++;
p = tire[p][t];
v[p].push_back(pos);
}
}
void dfs(int p, int pos)
{
if (pos == -1)
return;
int l = tire[p][0], r = tire[p][1];
ll sz0 = v[l].size(), sz1 = v[r].size();
ll ans = 0, temp = 0;
rep(i, 0, sz0 - 1)
{
if (!sz1)
break;
while (temp < sz1 && v[r][temp] < v[l][i])
temp++;
ans += temp;
}
dp[pos][0] += ans;
dp[pos][1] += sz1 * sz0 - ans;
if (l != 0)
dfs(l, pos - 1);
if (r != 0)
dfs(r, pos - 1);
}
int main()
{
int t;
sd(n);
rep(i, 1, n)
{
sd(a[i]);
insert(a[i], i);
}
dfs(0, 30);
rep(i, 0, 30)
{
if (dp[i][0] <= dp[i][1])
{
ans1 += dp[i][0];
}
else
{
ans1 += dp[i][1];
ans2 += (1 << i);
}
}
pldd(ans1, ans2);
return 0;
}