Manthan, Codefest 18 (rated, Div. 1 + Div. 2)
A - Packets
题意:给n个硬币,要求把这些硬币装在尽可能少的袋子里,并能表示出[1,n]的所有数,求袋子数。
题解:以前没想过为什么这个东西的本质是二进制,其实“用”和“不用”就是“1”和“0”,那么要表示[1,n]的所有数肯定需要各个二进制位都至少有1个,然后剩下假如有零头就零头自己装一个。
void test_case() {
int n;
scanf("%d", &n);
int x = 1;
while((1 << x) <= n)
++x;
printf("%d\n", x);
}
B - Reach Median
题意:给n(n是奇数)个数,每次操作给一个数+1或者-1,求最少的操作次数使得中位数恰好为s。
题解:排序。然后判断s是不是恰好在中位数,是则0,否则:若中位数大于s,则需要把这个数之前的比s大的数全部改成s;否则要把这个数之后的比s小的数全部改成s。可以出一个强化版。
int a[200005];
void test_case() {
int n, s;
scanf("%d%d", &n, &s);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
sort(a + 1, a + 1 + n);
int mid = (n + 1) / 2;
if(a[mid] == s) {
printf("0\n");
return;
} else if(a[mid] > s) {
ll sum = 0;
for(int i = 1; i <= mid; ++i) {
if(a[i] > s)
sum += a[i] - s;
}
printf("%lld\n", sum);
return;
} else {
ll sum = 0;
for(int i = mid; i <= n; ++i) {
if(a[i] < s)
sum += s - a[i];
}
printf("%lld\n", sum);
return;
}
}
C. Equalize
题意:给定n长度的01字符串s和t,对s进行尽可能少的操作使得它变为t。一次操作的cost为:直接修改cost=1,交换i,j位置的数cost=|i-j|。
题解:除了临位交换,其他的直接改。
int n;
char s[2000005];
char t[2000005];
void test_case() {
scanf("%d%s%s", &n, s + 1, t + 1);
int sum = 0;
for(int i = 1; i <= n - 1; ++i) {
if(s[i] != s[i + 1] && s[i] != t[i] && s[i + 1] != t[i + 1]) {
++sum;
swap(s[i], s[i + 1]);
} else if(s[i] != t[i]) {
++sum;
s[i] = t[i];
}
}
if(s[n] != t[n]) {
++sum;
s[n] = t[n];
}
printf("%d\n", sum);
}
D. Valid BFS?
题意:给一棵以1为根的树,验证一个序列是不是(其中一种合法的)BFS序。
题解:按照题目给的序列标记同一个节点各儿子的先后顺序,然后得到的BFS序必定是唯一的,再全文比较。
int n;
vector<int> G1[200005];
int pos[200005];
bool vis[200005];
set<pii> G2[200005];
queue<int> Q;
int ans[200005];
void bfs() {
Q.push(1);
vis[1] = 1;
int cnt = 0;
while(!Q.empty()) {
int u = Q.front();
ans[++cnt] = u;
Q.pop();
for(auto &i : G2[u]) {
int v = i.second;
if(vis[v])
continue;
Q.push(v);
vis[v] = 1;
}
}
if(cnt != n) {
puts("No");
return;
}
for(int i = 1; i <= n; ++i) {
if(ans[pos[i]] != i) {
puts("No");
return;
}
}
puts("Yes");
}
void test_case() {
scanf("%d", &n);
for(int i = 1; i <= n - 1; ++i) {
int u, v;
scanf("%d%d", &u, &v);
G1[u].push_back(v);
G1[v].push_back(u);
}
for(int i = 1; i <= n; ++i) {
int x;
scanf("%d", &x);
pos[x] = i;
}
for(int i = 1; i <= n; ++i) {
for(auto v : G1[i])
G2[i].insert({pos[v], v});
}
bfs();
}
E. Trips
题意:给一个n个点m条边的无向图,每条边是依次加入的,求每次加入边后,当前的图中后能选出的最多的点,使得他们的度数都不小于k。
题解:不知道正着能不能做,反着一定可以做,先把整个图构造出来,标记所有度数<k的点,循环删除直到剩下的点都满足度数不小于k,然后依次删除边,注意若一条边的其中一端被删除过那么就不要重复删除了。注意处理好删除点和删除边的联系就可以。
但是真正写的时候发现联系还挺复杂的,容易把边重复删除,用set就没这个问题了。
int n, m, k;
int U[200005], V[200005];
int d[200005];
set<int> G[200005];
bool vis[200005];
int ans[200005];
int cnt;
void DeleteV(int u) {
if(vis[u])
return;
vis[u] = 1;
/*printf("delete u=%d\n", u);
for(int j = 1; j <= n; ++j)
printf(" %d", d[j]);
printf("\n");*/
++cnt;
for(auto &v : G[u]) {
if(vis[v])
continue;
--d[u];
--d[v];
G[v].erase(u);
if(d[v] < k)
DeleteV(v);
}
G[u].clear();
}
void test_case() {
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= m; ++i) {
int u, v;
scanf("%d%d", &u, &v);
U[i] = u, V[i] = v;
G[u].insert(v);
G[v].insert(u);
++d[u];
++d[v];
}
for(int i = 1; i <= n; ++i) {
if(!vis[i] && d[i] < k)
DeleteV(i);
}
for(int i = m; i >= 1; --i) {
/*printf("i=%d\n", i);
for(int j = 1; j <= n; ++j)
printf(" %d", d[j]);
printf("\n");*/
ans[i] = n - cnt;
int u = U[i], v = V[i];
if(vis[u] || vis[v])
continue;
--d[u];
--d[v];
G[u].erase(v);
G[v].erase(u);
if(!vis[u] && d[u] < k)
DeleteV(u);
if(!vis[v] && d[v] < k)
DeleteV(v);
}
for(int i = 1; i <= m; ++i)
printf("%d\n", ans[i]);
}