1037A. Packets
n个硬币,问至少分成多少堆可以用这些堆来组合出1到n的所有硬币数。
分成2的幂次,且n为2的幂次时计数值加一,答案为
可以直接用log2()
函数,不会出错。
write((int)log2(read())+1);
1037B. Reach Median
程序:照他说的做
编程:照它说的做
给定n(奇数)个元素的数组和数字k,选择一个元素并加或减1需要付出1的代价,问将数组中位数变为k的最小代价。
最后的结果应当是排序后前n/2个元素小于等于k,第n/2+1个元素等于k,后n/2个元素大于等于k。这样变就好了。
int n = read(), k =read();
for(int i=1;i<=n;i++)
save[i] = read();
sort(save+1,save+n+1);
ll ans = 0;
for(int i=1;i<=n;i++)
{
if(i<=n/2)
ans += max(save[i]-k,0);
if(i==n/2+1)
ans += abs(k-save[i]);
if(i>n/2+1)
ans += max(k-save[i],0);
}
cout << ans << endl;
不应被0开头或1开头的数组所迷惑
1037C. Equalize
给两个长度相同的01串A和B,想要让A变成B,有两种操作:1.交换A[i]与A[j],代价 2.翻转A[i],代价1,求最小代价。
操作1仅在A[i]!=B[i]&&A[i+1]!=B[i+1]&&A[i]!=A[i+1]时使用
1038D. Valid BFS?
定义无向图的BFS操作,(使用队列,始终从1号点开始,随机顺序选取邻接节点)。现在给定一棵树和它节点的一个顺序数组,问是否可能是用BFS操作的访问顺序。
首先了解bfs的访问顺序:按深度访问,且每个连续部分的父亲都相同。这题解法很多,我的解法使用了第二个性质。
首先邻接表set存储,然后新建一个队列并把1入队(判断1是否是第一个元素)。
然后从第二个位置开始访问顺序数组,每次查询这个位置的元素是否在队头的邻接集合中,如果在就将这条边删除(两次)并将新元素入队,如果队头的邻接集合为空就出队,如果队头邻接集合不为空且新元素不在其中那么返回错误。
相当于双指针扫了一遍这个数组(队列),每次查询O(logn),总复杂度O(nlogn)
set<int> save[M];
int quen[M];
int main(void)
{
int n = read();
for(int i=1;i<n;i++)
{
int a = read();
int b = read();
save[a].insert(b);
save[b].insert(a);
}
for(int i=1;i<=n;i++)
quen[i] = read();
if(quen[1]!=1) return !printf("No\n");
queue<int> q;
q.push(1);
int id = 2;
while(id<=n)
{
if(save[q.front()].size()==0)
{
q.pop();
}
else if(save[q.front()].count(quen[id])==0)
{
return !printf("No\n");
}
else
{
save[q.front()].erase(quen[id]);
save[quen[id]].erase(q.front());
q.push(quen[id]);
id++;
}
}
printf("Yes\n");
return 0;
}
事实上,双指针写while+if总是可读性更高的。
1037E. Trips
给定人数n(2e5),天数m(2e5),阈值k,每一天早上会有两个陌生人成为朋友,朋友不具有传递性。问1到m天分别有多少个人是快乐的,当一个人有k个快乐的朋友时他就是快乐的。
首先只考虑最后一天的情况,假设所有人是快乐的,然后检查所有人并排除朋友数不到k的人,每排除一个人就将他所有朋友的朋友数减一,并且和这个不快乐的人断绝朋友关系再对这个朋友递归地进行检查。
因为每个人最多只会被排除一次,每当一个人被排除时才会检查他的朋友们,所以只执行了O(n+m)次操作,可以用set解决。
然后就可以倒着做,每次删去一条边,然后检查这两个节点即可。
总的复杂度仍为O(nlogn),证明同上。
本题为什么不能正着扫?
因为每次加边都得判断它所连点的所连点的所连点……复杂度非常高,甚至不如全判断一次。
pair<int,int> save[M];
set<int> st[M]; //邻接表
set<int> go;
int n,m,k;
void check(int id)
{
if(go.count(id) && (int)st[id].size()<k)
{
go.erase(id);
for(auto x : st[id])
{
st[x].erase(id);
check(x);
}
st[id].clear();
}
}
int ans[M];
int main(void)
{
n = read(), m = read(), k = read();
for(int i=1;i<=m;i++)
{
int a = read(), b = read();
save[i].first = a, save[i].second = b;
st[a].insert(b), st[b].insert(a);
}
for(int i = 1;i<=n;i++)
go.insert(i);
for(int i = 1;i<=n;i++)
check(i);
ans[m] = go.size();
for(int i = m;i>=2;i--)
{
int a = save[i].first, b = save[i].second;
st[a].erase(b), st[b].erase(a);
check(a), check(b);
ans[i-1] = go.size();
}
for(int i = 1;i<=m;i++)
printf("%d\n",ans[i] );
return 0;
}
要输出若干次答案的,可以先考虑最后一次的答案,再回头倒跑或者二分。
1037F. Maximum Reduction
待续