A,B略过
C. Powers Of Two
题意:给出n,k将数字n分解为k部分都是2^x的数字之和,eg 9 = 1 + 2 + 2 + 4。
如果能,第一行输出YES,第二行输出这k个数字,顺序不限,如果不能,输出NO
思路:将n进行二进制分解,首先n的最高位不能大于k,大于k显然无解,然后是一个贪心的想法例如 9 = 1001 = 8 + 1 ,我们可以把8,1放进一个大到小的优先队列,每次取出队首,将队首除以2,得到两个为2的幂次方的数字重新放入优先队列,循环往复即可。
总结:思维训练题。
AC code:
#include<bits/stdc++.h>
using namespace std;
int bit[31];
int main(){
int n,k;
cin >> n >> k;
if(k > n){
puts("NO");
return 0;
}
int tot = 0;
for(int i = 0; i <= 30; ++i){
if(n & (1 << i)){
bit[tot++] = i;
}
}
if(tot > k){
puts("NO");
return 0;
}
puts("YES");
priority_queue<int, vector<int>, less<int> > q;
for(int i = 0; i < tot; ++i){
q.push(1 << bit[i]);
}
while(q.size() < k){
int cur = q.top(); q.pop();
if(cur >= 2){
q.push(cur >> 1);
q.push(cur >> 1);
}
}
while(!q.empty()){
printf("%d ",q.top());
q.pop();
}
return 0;
}
D. Circular Dance
题意:n个人环形排列,第i个人只知道它后面有两个人的编号j,k,但不知道j,k的相对位置。求一个合理的排列。
思路:从样例看,如果我们选择第一个孩子1作为起始,这时候它后面有3,5;看看3后面是否有5,或者5后面是否有3,一个个递推,根据第i个孩子去确定第i+1个孩子。
AC code:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 100;
int a[maxn][2],p[maxn];
bool vis[maxn];
int main(){
int n;
cin >> n;
for(int i = 1; i <= n; ++i){
scanf("%d%d",&a[i][0],&a[i][1]);
}
printf("%d",1);
vis[1] = 1;
int cnt = 1;
int nex = 1;
while(cnt++ < n){
int u = a[nex][0], v = a[nex][1];
if((a[u][0] == v || a[u][1] == v) && !vis[u]){
p[nex] = u;
}else{
p[nex] = v;
}
nex = p[nex];
vis[nex] = 1;
printf(" %d",nex);
}
putchar('\n');
return 0;
}
E. Almost Regular Bracket Sequence
题意:给定一个长度为n的括号序列,求出一个修改位置i使得改为这个位置i后使得括号序列是对称平衡的,如果没有就输出0.
思路:注意是一个位置i,没怎么做过括号题,都是补的,想法是把左括号值当作1,右括号值当作-1,如果能够使得存在一个修改位置修改后使得括号序列是平衡的,那么序列总和sum 值要么为2,要么为-2。sum = 2说明左括号比右括号多,需要将一个左括号改成右括号,sum = -2 同理。
先讨论sum = 2的情况,这时候我们可以通过统计该序列前缀和和前缀左括号数的方法求解出该位置,从sum = 0开始统计,如果统计过程中发现某个位置前缀和sum 小与0,但总和sum = 2,说明这个序列前面右括号多,后边左括号多,是没法平衡的,直接无解。如果统计中间过程 sum < 2说明这个位置前面里面左括号多,这个位置pos的左边是没有解的,前缀括号数ans = 0.
sum = -2 我们可以逆序转换值的方法将其变成sum = 2的情况。
AC code:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 100;
char s[maxn];
int a[maxn];
int main(){
int n;
cin >> n;
scanf("%s",s+1);
int sum = 0;
for(int i = 1; i <= n; ++i){
a[i] = (s[i] == '(' ) ? 1 : -1;
sum += a[i];
}
if(sum != 2 && sum != -2){
puts("0");
return 0;
}
if(sum == -2){
for(int i = 1; i <= n; ++i) a[i] = - a[i];
reverse(a + 1, a + n + 1);
}
sum = 0;
int ans = 0;
for(int i = 1; i <= n; ++i){
sum += a[i];
if(a[i] > 0) ++ans;
//cout << ans << ' ';
if(sum < 2) ans = 0;
if(sum < 0){
ans = 0; break ;
}
}
cout << ans << endl;
}
题意:给出n个点,每个点权值a[i],和m条带权值的边,你可以使用这些边,连接两个点,也可以不用这些边连接两个点u,v,新边权值为点权之和a[u] + a[v];然后求出一颗最小生成树。
思路:最小生成树,很自然的想到kruskal算法,但这题目还有一些额外可添加的点权之和边,那你加进去当然是加入点权之和尽量小的边,所以就是在原图中加完m条边后,找出点权最小的点st,从这个点和其他所有点连权值为点权之和的新边,然后跑kruskal即可。
AC code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template <typename T>
void read(T &x){
x = 0;
int p = 1; char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) if(c == '-') p = -1;
for(; c >= '0' && c <= '9'; c = getchar())
x = (x << 1) + (x << 3) + (c ^ 48);
}
const int INF = 0x3f3f3f3f;
const int maxn = 2e5 + 100;
int fa[maxn];
int Find(int x){
return x == fa[x] ? x : fa[x] = Find(fa[x]);
}
struct Node{
int u,v,next;
ll w;
bool operator < (const Node & d) const{
return w < d.w;
}
}edge[maxn << 2];
int tot = 0, head[maxn];
void init(int n){
tot = 0;
for(int i = 0; i <= n; ++i){
fa[i] = i, head[i] = -1;
}
}
inline void addedge(int u,int v,ll w){
edge[tot].u = u;
edge[tot].v = v;
edge[tot].next = head[u];
edge[tot].w = w;
head[u] = tot++;
}
ll a[maxn];
void kruskal(int n){
sort(edge,edge + tot);
ll ans = 0;
int cnt = 0;
for(int i = 0; i < tot; ++i){
int u = Find(edge[i].u), v = Find(edge[i].v);
if(u == v) continue ;
ans += edge[i].w;
fa[u] = v;
if(++cnt == n - 1) break ;
}
printf("%lld\n",ans);
}
int main(){
int n,m;
read(n),read(m);
init(n + 1);
int st;
ll minn = INF;
for(int i = 1; i <= n; ++i){
read(a[i]);
if(minn > a[i]) minn = a[i], st = i;
}
int u,v;
ll w;
for(int i = 1; i <= m; ++i){
read(u),read(v),read(w);
addedge(u,v,w);
}
for(int i = 1; i <= n; ++i){
if(i == st) continue;
addedge(st,i,a[st] + a[i]);
}
kruskal(n);
return 0;
}