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;
}

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