Codeforces Round #592 Div. 2

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
在这里插入图片描述
题解:
相当于求
xw+yd=px+ynx \cdot w + y \cdot d = p\\x+y \le n
如果有一组x,yx,y满足xw+yd=px \cdot w + y \cdot d = p
xw+yd=xw+(y/ww+y%w)d=(x+y/wd)w+(y%w)d=px\cdot w+y\cdot d = x\cdot w+(\lfloor y/w \rfloor \cdot w + y \%w)\cdot d =(x+ \lfloor y/w \rfloor \cdot d) * w + (y\%w) d =p
x+y/wd+y%wx+y/ww+y%w=x+yx + \lfloor y/w \rfloor \cdot d + y \% w \le x+ \lfloor y/w \rfloor \cdot w + y \%w=x+y,即有一组和更小的且y<wy < w的满足条件。所以我们只要从0开始到w1w-1枚举yy即可

#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。

假设我们将前ii个数移动到了区间[a[i],a[i+1]][a[i] ,a[i + 1]]中,那么倒数ii个数也要移动到区间[a[ni],a[ni+1]][a[n - i],a[n - i + 1]]中。不然的话,不妨假设只移动末尾j<ij<i个数。我们移动jj个数只需要j个操作,而移动前面的i个数要i个操作。将前i个数进行回退,操作后j个数,不会产生更坏的结果。
所以,我们只需要枚举移动到的位置ii,将两端ii个数进行移动,一直到不能再移动。

#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轮以后每个点都是什么颜色。
在这里插入图片描述
题解:注意如果两个颜色相同的连在一起,那么它们的颜色就不会再发生变化。我们只需要只考虑如下两种情况,其他的都可以等同于这两种
wwbwbwbwbbwwbwbwbwwwwbwbwbwbb或者wwbwbwbww
第一个的话,接下来几轮的变化如下
wwwbwbwbbbwwwwbwbbbbwwwwwbbbbbwwwbwbwbbb\\ wwwwbwbbbb\\ wwwwwbbbbb
第二个的话,接下来几轮的变化如下
wwwbwbwwwwwwwbwwwwwwwwwwwwwwwwbwbwww\\ wwwwbwwww\\ wwwwwwwww
可以看到每一轮,连续的那部分都在往周围扩张,每次长度为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个参加比赛。每场两个人,每组第ii个人完成比赛的时间是ii。每场比赛的时间是两个选手中用时较长的。你可以给每组的选手调整顺序,让比较时间变长。 给定一个k,要求时间不超过k的最长比赛时间,以及对应的每组人员顺序。

题解:因为是两两比赛,所以我们可以先固定第一组的顺序为1,2,…,n。然后调整第二组即可。最小的比赛时间是sum1=1+2+...+n=n(n+1)/2sum1 = 1 + 2 + ...+n = n(n+1)/2所以当kk小于这个数时,就不存在方案。当k大于等于这个数时,就存在。最大的比赛时间是什么,就是当第二组倒序的时候n,n1,...,1n,n - 1,...,1,记为sum2sum2

在最小比赛sum1sum1和最大比赛时间sum2sum2中的任何时间都是可达的(可以通过一种顺序得到)。

我们可以从排列1,...,n1,...,n开始,将最小的往后移动,移动1步变成2,1,3,...,n2,1,3,...,n时间比sum1sum1大1,移动到变成2,3,...,n,12,3,...,n,1时,和比sum1sum1n1n - 1

接下来移动2,可以看到当2的位置在2以后的时候开始每次增1,一直到2移动到n - 1的位置。产生的和的增加可以从1到n - 1 - 2

当移动i的时候,产生的和的增加数可以用从1一直到n - 2 * i + 1 (当n - 2i + 1 <= 0时,就不能再增加了,即已经移动超过一半,前面部分的顺序无关紧要)。比如
1,2,3,4,53,4,5,2,11,2,3,4,5\\ 3,4,5,2,1
前面3,4,53,4,5的次序怎么改都和最大的5,4,3,2,15,4,3,2,1一样。
所以我们能够每次把一个数往后移动,一直到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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章