https://codeforces.com/contest/1328
A - Divisibility Problem
记得取模
#include
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
const int MAXN = 2e5 + 5;
int main()
{
int T = 1;
sc("%d", &T);
while (T--)
{
ll a, b;
sc("%lld%lld", &a, &b);
pr("%lld\n", (b - a % b) % b);
}
}
B - K-th Beautiful String
假设这个位置放 a ,看看后面有多少种方法,就是组合数
#include
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
const int MAXN = 2e5 + 5;
int main()
{
int T = 1;
sc("%d", &T);
while (T--)
{
ll n, k;
sc("%lld%lld", &n, &k);
k--;
ll a = n - 2, b = 2;
for (int i = 1; i <= n; i++)
{
ll ans = 0;
if (b == 2)
ans = (a - 1 + b) * (a - 1 + b - 1) / 2;
else
ans = a;
if (ans > k && a != 0)
{
pr("a");
a--;
}
else
{
k -= ans;
pr("b");
b--;
}
}
pr("\n");
}
}
C - Ternary XOR
遍历,遇到 0 2 平分,遇到 1 将 1 给第一个,后面全给第二个数字
#include
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
const int MAXN = 2e5 + 5;
char s[MAXN];
char q[MAXN], w[MAXN];
int main()
{
int T = 1;
sc("%d", &T);
while (T--)
{
sc("%*d");
sc("%s", s);
int len = strlen(s);
int cnt1 = 0, cnt2 = 0;
for (int i = 0; i < len; i++)
{
if (s[i] == '2')
{
q[cnt1++] = '1';
w[cnt2++] = '1';
}
else if (s[i] == '0')
{
q[cnt1++] = '0';
w[cnt2++] = '0';
}
else
{
q[cnt1++] = '1';
w[cnt2++] = '0';
while (cnt1 < len)
q[cnt1++] = '0';
while (cnt2 < len)
{
w[cnt2] = s[cnt2];
cnt2++;
}
break;
}
}
q[cnt1] = '\0';
w[cnt2] = '\0';
pr("%s\n%s\n", q, w);
}
}
D - Carousel
n 个数字组成一个圈,相邻的不同的数字不可用相同的颜色,求最少颜色
如果存在相邻并且相同的数字,答案是1 或者 2
判掉 1 的情况,剩下的 2 的情况,从他们中间断开填入颜色 1 2 1 2 填即可。
如果不存在相邻的数字,分奇偶,偶数直接 1 2 1 2 填入颜色即可,奇数,需要三种,随便一个3 ,剩下的 1 2 1 2
比赛时犯了一个傻逼错误,然后卡题了,然后后面的题都不想看了(反正unrate)
#include
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
const int MAXN = 2e5 + 5;
int a[MAXN];
int ans[MAXN];
int main()
{
int T = 1;
sc("%d", &T);
for (int l = 1; l <= T; l++)
{
int n;
sc("%d", &n);
for (int i = 1; i <= n; i++)
{
sc("%d", &a[i]);
}
if (n == 1)
{
pr("1\n1\n");
continue;
}
int f = 0;
for (int i = 1; i <= n; i++)
{
if (i == n)
{
if (a[i] == a[1])
{
f = n;
break;
}
}
else if (a[i] == a[i + 1])
{
f = i;
break;
}
}
if (f == 0)
{
if (n % 2 == 0)
{
pr("2\n");
for (int i = 1; i <= n; i++)
{
if (i & 1)
pr("1 ");
else
pr("2 ");
}
pr("\n");
}
else
{
pr("3\n");
for (int i = 1; i < n; i++)
{
if (i & 1)
pr("1 ");
else
pr("2 ");
}
pr("3 ");
pr("\n");
}
}
else
{
for (int i = 2; i <= n; i++)
{
if (a[i] != a[1])
{
pr("2\n");
if (n % 2 == 0)
{
ans[f] = 1;
if (f + 1 <= n)
ans[f + 1] = 2;
else
ans[1] = 2;
}
else
{
ans[f] = 1;
if (f + 1 <= n)
ans[f + 1] = 1;
else
ans[1] = 1;
}
int t = f - 1;
while (t >= 1)
{
ans[t] = 3 - ans[t + 1];
t--;
}
t = f + 2;
while (t <= n)
{
ans[t] = 3 - ans[t - 1];
t++;
}
for (int i = 1; i <= n; i++)
pr("%d ", ans[i]);
pr("\n");
goto qwe;
}
}
pr("1\n");
for (int i = 1; i <= n; i++)
pr("1 ");
pr("\n");
}
qwe:;
}
}
E - Tree Queries
给你一棵树,每次询问若干个点,问是否存在一条从 1 开始的链,使得这些点距离链的距离小于等于 1
从根节点开始的链,距离小于等于1,考虑将所有点变成他的爸爸,然后就变成了这个这些点是否在一条链上,然后倍增lCA即可
由于我不会倍增LCA,但我会树剖。
#include
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
const int MAXN = 2e5 + 5;
struct edge
{
int to;
int nex;
}e[MAXN * 2];
int head[MAXN], tot;
int son[MAXN], fa[MAXN], dep[MAXN], sz[MAXN];
int p[MAXN], fp[MAXN], top[MAXN], pos;
void init()
{
memset(head, -1, sizeof(head));
tot = 1;
memset(son, -1, sizeof(son));
pos = 1;
}
void add(int a, int b)
{
e[tot] = edge{ b,head[a] };
head[a] = tot++;
}
void dfs1(int u, int f)
{
fa[u] = f;
dep[u] = dep[f] + 1;
sz[u] = 1;
for (int i = head[u]; i + 1; i = e[i].nex)
{
int v = e[i].to;
if (v == f)
continue;
dfs1(v, u);
sz[u] += sz[v];
if (son[u] == -1 || (sz[son[u]] <= sz[v]))
son[u] = v;
}
}
void dfs2(int u, int tp)
{
top[u] = tp;
p[u] = pos;
fp[pos] = u;
pos++;
if (son[u] == -1)
return;
dfs2(son[u], tp);
for (int i = head[u]; i + 1; i = e[i].nex)
{
int v = e[i].to;
if (v == fa[u] || v == son[u])
continue;
dfs2(v, v);
}
}
int lca(int a, int b)
{
int t1 = top[a];
int t2 = top[b];
while (t1 != t2)
{
if (dep[t1] < dep[t2])
{
swap(t1, t2);
swap(a, b);
}
a = fa[t1];
t1 = top[a];
//
}
if (dep[a] < dep[b])
return a;
else
return b;
}
int main()
{
init();
int n, m;
sc("%d%d", &n, &m);
for (int i = 1; i < n; i++)
{
int a, b;
sc("%d%d", &a, &b);
add(a, b);
add(b, a);
}
dfs1(1, 0);
dfs2(1, 1);
while (m--)
{
int num, t;
sc("%d", &num);
vectorv;
for (int i = 0; i < num; i++)
{
sc("%d", &t);
if (t != 1)
v.push_back(fa[t]);
}
sort(v.begin(), v.end(), [](int q, int w) {
return dep[q] > dep[w];
});
num = v.size();
for (int i = 0; i < num - 1; i++)
{
int t = lca(v[i], v[i + 1]);
if (t != v[i + 1])
{
pr("NO\n");
goto qwe;
}
}
pr("YES\n");
qwe:;
}
}
F - Make k Equal
给你 n 个数字,每次可以选择一个最大的数字-1,或者最小的数字+1,问最少操作多少次使得有 k 个数字相等。
就是只考虑从左往右,然后只考虑从右往左,最后考虑两个都取的情况,就是将多余的放回左边或者右边。
记得先判不需要移动的情况,不然后面可能会出现负数。
在两个都取的情况,只能将多余的放回左边-1或者右边+1,因为我们在这种情况下考虑的是两部分都对 k个相等 产生贡献。
#include
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
const int MAXN = 2e5 + 5;
ll a[MAXN];
ll num[MAXN];
#define Pair pair//第一个值是个数,第二个值是花费
Pair val1[MAXN], val2[MAXN];
ll t[MAXN];
int main()
{
int n, k;
sc("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
{
sc("%lld", &a[i]);
t[i] = a[i];
}
sort(t + 1, t + 1 + n);
int qq = unique(t + 1, t + 1 + n) - (t + 1);
for (int i = 1; i <= n; i++)
{
a[i] = lower_bound(t + 1, t + 1 + qq, a[i]) - t;
num[a[i]]++;
}
for (int i = 1; i <= qq; i++)
{
if (num[i] >= k)
{
pr("0\n");
return 0;
}
}
ll ans = 1e18;
for (int i = 1; i <= qq; i++)
{
val1[i].first = val1[i - 1].first + num[i];
val1[i].second = val1[i - 1].first * (t[i] - t[i - 1]) + val1[i - 1].second;
if (val1[i].first >= k)
ans = min(ans, val1[i].second - (val1[i].first - k));
}
for (int i = qq; i >= 1; i--)
{
val2[i].first = val2[i + 1].first + num[i];
val2[i].second = val2[i + 1].first * (t[i + 1] - t[i]) + val2[i + 1].second;
if (val2[i].first >= k)
ans = min(ans, val2[i].second - (val2[i].first - k));
}
for (int i = 1; i <= qq; i++)
if (val1[i].first + val2[i].first - num[i] >= k)
ans = min(ans, val1[i].second + val2[i].second - (val1[i].first + val2[i].first - num[i] - k));
pr("%lld\n", ans);
}