1244A Pens and Pencils
题意:给定a,b,c,d,k。有两种物品,一个物品1的价值是c,物品2的价值是d。揹包容量至多可以装k个物品。问有没有装物品的方案使得物品1的价值至少位a,物品2的价值至少位b。有输出合法的一种,没有输出-1.
题解:直接求
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
int a,b,c,d,k;
cin>>t;
while(t--){
cin >> a >> b >> c >> d >> k;
int pen = a / c + (a % c != 0);
int pencil = b / d + (b % d != 0);
if(pen + pencil > k){
puts("-1");
}else{
cout<<pen<<" "<<pencil<<endl;
}
}
return 0;
}
1244B Rooms and Staircases
题意:Nikolay 有个两层楼的房子,每层n个房间。每个房间和左右相邻房间有个门可以互通。一层和二层有一些梯子。有梯子的不同层房间可以互通。如下图。问Nikolay从任一房间开始,每次可以走到互通的房间中去,能到达的最大房间数。期间不能进入一个房子两次。
题解:贪心。如果没有梯子,显然数目就是n。如果有一个梯子,那么就是拐一个弯,如下图。
如果有大于等于两个梯子。不难看出中间的可以不予考虑,最大的依然是考虑边缘的两个梯子,从一端开始,拐最长的弯。
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t, n;
string s, s1;
cin >> t;
while(t--){
cin >> n >> s;
s1 = s;
reverse(s1.begin(), s1.end());
string::size_type pos1 = s.find('1');
string::size_type pos2 = s1.find('1');
if(pos1 == s.npos) cout << n << endl;
else cout<< 2 * n - 2 * min(pos1, pos2) << endl;
}
return 0;
}
1244C The Football Season
题意:给定n,p,w,d
求解满足下式的非负整数x,y,z
题解:
相当于求
如果有一组满足,
则
且,即有一组和更小的且的满足条件。所以我们只要从0开始到枚举即可
#include<bits/stdc++.h>
using namespace std;
int main(){
long long n, p, w, d;
cin >> n >> p >> w >> d;
long long x = -1,y;
for(int i = 0;i < w; ++i){
long long r = p - d * i;
if(r < 0) break;
if(r % w) continue;
y = i;
x = r / w;
break;
}
if(x == -1 || x + y > n) puts("-1");
else cout<<x<<" "<<y<<" "<<n - x - y<<endl;
return 0;
}
1244D Paint the Tree
题意:给定一棵树n个点,每个点可以着三种颜色。每个点着每种颜色都有一个cost(共3n个cost)。求树的最小cost着色方案,需要满足相邻的三个点的颜色不同。如果不存在方案,输出-1
题解:首先,如果有一个点的度数大于等于3,那是不可能有合法方案的。所以存在合法方案的一定是一条链。对于一条链的情况,着色方案由前两个点完全确定。所以我们可以枚举前两个点的6种着色方案,求最小cost方案。代码写挫了。稍后改
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+3;
int d[N], C[3][N], C2[3][N], color[N];
int id[N];
vector<int> edge[N];
void dfs(int x, int fa, int depth){
id[depth] = x;
for(int i = 0;i < edge[x].size(); ++i){
int u = edge[x][i];
if(vis[u]) continue;
dfs(u, x, depth + 1);
}
}
int get_next_color(int col1, int col2){
bool col_meet[3] = {0};
col_meet[col1] = col_meet[col2] = true;
for(int i = 0;i < 3; ++i) if(!col_meet[i]) return i;
}
int main(){
int n;
cin >> n;
memset(d, 0, (n + 1) * sizeof(int));
for(int _ = 0; _ < 3; ++_)
for(int i = 1;i <= n; ++i) scanf("%d",&C[_][i]);
int ans = 0, root = -1;
for(int i = 1;i < n; ++i){
int u,v;
cin >> u >> v;
++d[u];
++d[v];
edge[u].push_back(v);
edge[v].push_back(u);
if(d[u] > 2 || d[v] > 2) ans = -1;
}
for(int i = 1;i <= n; ++i){
if(d[i] == 1){
root = i;
break;
}
}
if(ans == -1) puts("-1");
else{
if(n == 1){
int min_c = min(C[0][1],min(C[1][1],C[2][1]));
if(min_c == C[0][1]) puts("1");
else if(min_c == C[1][1]) puts("2");
else puts("3");
return 0;
}
dfs(root, -1, 0);
for(int i = 1; i <= n; ++i){
for(int j = 0;j < 3; ++j)
C2[j][i] = C[j][id[i - 1]];
}
long long min_v = 1e18;
int col1 = -1, col2 = -1;
for(int c = 0; c < 9; ++c){
int p = c % 3, pp = (c / 3) % 3;
if(p == pp) continue;
long long cur_v = C2[p][2] + C2[pp][1];
for(int idx = 3; idx <= n; ++idx){
int k = get_next_color(p, pp);
cur_v += C2[k][idx];
pp = p, p = k;
}
if(min_v > cur_v){
min_v = cur_v;
col1 = c % 3;
col2 = (c / 3) % 3;
}
}
color[id[0]] = col2, color[id[1]] = col1;
int p = col1, pp = col2;
for(int i = 2; i < n; ++i){
int k = get_next_color(p, pp);
color[id[i]] = k;
pp = p, p = k;
}
cout<<min_v<<endl;
for(int i = 1;i <= n; ++i) cout<<color[i] + 1<<" ";
cout<<endl;
}
return 0;
}
1244E Minimizing Difference
题意:给定一个正整数数组,长度为n,和一个整数k
你可以每次对数组中的一个元素进行加1或者减1的操作,最多执行k次。问经过一系列操作后数组的最大值和最小值的差最小是多少。
题解:贪心。先将数组排序。首先要使得差值减小,一定将两边的往中间移动。 所以我们先从两端开始移动。将i个数移动一次的代价是i。
假设我们将前个数移动到了区间中,那么倒数个数也要移动到区间中。不然的话,不妨假设只移动末尾个数。我们移动个数只需要j个操作,而移动前面的i个数要i个操作。将前i个数进行回退,操作后j个数,不会产生更坏的结果。
所以,我们只需要枚举移动到的位置,将两端个数进行移动,一直到不能再移动。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 3;
long long a[N];
int main() {
long long n,k;
cin >> n >> k;
for(int i = 1;i <= n; ++i) cin >> a[i];
sort(a + 1, a + n + 1);
long long ans = 0;
for(int i = 1;i <= n / 2; ++i){
int j = n - i + 1;
long long r = (a[i + 1] - a[i] + a[j] - a[j - 1]) * i;
if(r <= k) k -= r;
else{
ans = a[j] - a[i] - k / i;
break;
}
}
cout<<ans<<endl;
return 0;
}
1244F Chips
题意:给一个环,每个节点一开始为黑色或者白色。每一轮,每个节点的颜色根据它旁边的两个节点以及它自己的颜色进行改变。如果有两个黑色,它就变成黑色。两个白色就变成白色。问k轮以后每个点都是什么颜色。
题解:注意如果两个颜色相同的连在一起,那么它们的颜色就不会再发生变化。我们只需要只考虑如下两种情况,其他的都可以等同于这两种
第一个的话,接下来几轮的变化如下
第二个的话,接下来几轮的变化如下
可以看到每一轮,连续的那部分都在往周围扩张,每次长度为1.所以对于每个点,我们只需要求和它距离最近的那个连续段。如果距离<= k,那么它就变成最近连续段的那种颜色。如果距离>k。那么当k为奇数时,颜色进行翻转,否则不变。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 1;
bool fixed_pos[N];
int left_dis[N], right_dis[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,k;
string s;
cin >> n >> k >> s;
for(int i = 0;i < n; ++i){
if(s[i] == s[(i - 1 + n) % n] || s[i] == s[(i + 1) % n])
fixed_pos[i] = true, left_dis[i] = right_dis[i] = INT_MAX;
}
for(int i = 0;i < n; ++i){
if(!fixed_pos[i]) continue;
left_dis[i] = right_dis[i] = 0;
for(int j = (i + 1) % n; !fixed_pos[j]; j = (j + 1) % n)
left_dis[j] = left_dis[(j - 1 + n) % n] + 1;
for(int j = (i + n - 1) % n; !fixed_pos[j]; j = (j + n - 1) % n)
right_dis[j] = right_dis[(j + 1) % n] + 1;
}
for(int i = 0; i < n; ++i){
if(min(left_dis[i], right_dis[i]) > k){
bool cur_char_is_w = (s[i] == 'W') ^ (k & 1);
cout<<(cur_char_is_w ? 'W' : 'B');
}else{
int pos = (i - left_dis[i] + n) % n;
//不连续段wbwb或者bwbw一定是偶数的,所以不会产生left_dis[i] == right_dis[i]这种情况
if(left_dis[i] > right_dis[i])
pos = (i + right_dis[i]) % n;
cout<<s[pos];
}
}
return 0;
}
1244G Running in Pairs
题意:有两组人,共2n个参加比赛。每场两个人,每组第个人完成比赛的时间是。每场比赛的时间是两个选手中用时较长的。你可以给每组的选手调整顺序,让比较时间变长。 给定一个k,要求时间不超过k的最长比赛时间,以及对应的每组人员顺序。
题解:因为是两两比赛,所以我们可以先固定第一组的顺序为1,2,…,n。然后调整第二组即可。最小的比赛时间是所以当小于这个数时,就不存在方案。当k大于等于这个数时,就存在。最大的比赛时间是什么,就是当第二组倒序的时候,记为。
在最小比赛和最大比赛时间中的任何时间都是可达的(可以通过一种顺序得到)。
我们可以从排列开始,将最小的往后移动,移动1步变成时间比大1,移动到变成时,和比大。
接下来移动2,可以看到当2的位置在2以后的时候开始每次增1,一直到2移动到n - 1的位置。产生的和的增加可以从1到n - 1 - 2
当移动i的时候,产生的和的增加数可以用从1一直到n - 2 * i + 1 (当n - 2i + 1 <= 0时,就不能再增加了,即已经移动超过一半,前面部分的顺序无关紧要)。比如
前面的次序怎么改都和最大的一样。
所以我们能够每次把一个数往后移动,一直到k或者最大时间。
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
long long n,k;
cin >> n >> k;
if(n * (n + 1) / 2 > k) {puts("-1"); return 0;}
long long tot_sum = n * (n + 1) / 2;
k -= tot_sum;
int pos;
for(pos = 1;pos < n; ++pos){
if(n - 2 * pos + 1 <= 0) continue;
if(n - 2 * pos + 1 <= k){
tot_sum += n - 2 * pos + 1;
k -= n - 2 * pos + 1;
}else{
break;
}
}
if(pos >= n)
cout << tot_sum <<endl;
else
cout << tot_sum + k<<endl;
for(int i = 1; i <= n; ++i) cout<<i<<" "; cout<<endl;
if(pos >= n){
for(int i = n; i >= 1; --i) cout<<i<<" "; cout<<endl;
}else{
if(k > 0){
//前面正序的一段
for(int i = pos + 1; i <= 2 * pos + k - 1; ++i) cout<<i<<" ";
//pos的位置
cout<<pos<<" ";
for(int i = 2 * pos + k; i <= n; ++i) cout<<i<<" ";
//后面倒序的一段
for(int i = pos - 1; i >= 1; --i) cout<<i<<" ";
cout<<endl;
}else{
for(int i = pos; i <= n; ++i) cout<<i<<" ";
for(int i = pos - 1; i >= 1; --i) cout<<i<<" ";
cout<<endl;
}
}
return 0;
}