VP Codeforces Round 896 (Div. 2)


A. Make It Zero

题意:给你一个数组,每次操作一个区间,让这个区间的数都变成区间的异或和,操作不能超过8次,使得数组全变成0。

如果数组是偶数,直接操作两次\([1, n]\)就行了。
如果数组是奇数,也是先操作一下\([1, n]\),这时数组都变成了一样的数,然后操作\([1, n - 1]\),前\(n-1\)个就变成0了,然后操作两次\([n - 1, n]\)就行。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    std::vector<std::pair<int, int>> ans;
    ans.push_back({1, n});
    if (n & 1) {
    	ans.push_back({1, n - 1});
    	ans.push_back({n - 1, n});
    	ans.push_back({n - 1, n});
    } else {
    	ans.push_back({1, n});
    }

    std::cout << ans.size() << "\n";
    for (auto & [l, r] : ans) {
    	std::cout << l << " " << r << "\n";
    }
}

B. 2D Traveling

题意:有\(n\)个坐标,前\(k\)个坐标之间可以不费代价的任意行走。如果两个坐标中一个不是前\(k\)个,那么代价就是曼哈顿距离。求\(a\)\(b\)的最小代价。

两个点直接到达肯定是最短的,经过中间一个点再取终点肯定不优。但现在前\(k\)个点可以互相到达,那么可以选一个到\(a\)最短的点和到\(b\)最短的点,因为这两个点之间不消耗代价,可能更优。

点击查看代码
void solve() {
    int n, k, a, b;
    std::cin >> n >> k >> a >> b;
    -- a, -- b;
    std::vector<i64> x(n), y(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> x[i] >> y[i];
    }

    if (a > b) {
    	std::swap(a, b);
    }

    if (a < k && b < k) {
    	std::cout << 0 << "\n";
    } else {
    	i64 mina = 1e18, minb = 1e18;
    	for (int i = 0; i < k; ++ i) {
    		mina = std::min(mina, std::abs(x[i] - x[a]) + std::abs(y[i] - y[a]));
    		minb = std::min(minb, std::abs(x[i] - x[b]) + std::abs(y[i] - y[b]));
    	}

    	if (a < k) {
    		mina = 0;
    	}

    	i64 ans = std::min(mina + minb, std::abs(x[a] - x[b]) + std::abs(y[a] - y[b]));
    	std::cout << ans << "\n";
    } 
}

C. Fill in the Matrix

题意:构造一个\(n\times m\)的数组,使得每一行都是\([0, n - 1]\)的排列,然后\(v_j\)代表第\(j\)列的\(mex\),总价值就是所有\(v_j\)\(mex\)。求一个方案使得最终\(mex\)最大。

赛时手玩了一下就懂了。
0 1 2 3 4 5
5 0 1 2 3 4
4 5 0 1 2 3
3 4 5 0 1 2
2 3 4 5 0 1
0 1 2 3 4 5
就是从\((i, i)\)开始放\(0\)循环到\((i, i-1)\)依次放数。放到\(m-1\)行就行了,发现前\(m-1\)行就凑出了\(mex\)最大。剩下的行跟第一行一样就行了。

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;

    std::vector a(n, std::vector<int>(m));
    for (int i = 0; i < n; ++ i) {
    	if (i >= m - 1) {
    		for (int j = 0; j < m; ++ j) {
    			a[i][j] = j;
    		}
    		continue;
    	}
    	int x = 0;
    	for (int j = i; j < m; ++ j) {
    		a[i][j] = x ++ ;
    	}

    	for (int j = 0; j < i; ++ j) {
    		a[i][j] = x ++ ;
    	}
    }

    int ans = 0;
    std::set<int> row;
    for (int j = 0; j < m; ++ j) {
    	std::set<int> col;
    	int mex = 0;
    	for (int i = 0; i < n; ++ i) {
    		col.insert(a[i][j]);
    		while (col.count(mex)) {
    			++ mex;
    		}
    	}
    	// std::cout << mex << "\n";

    	row.insert(mex);
    	while (row.count(ans)) {
    		++ ans;
    	}
    }

    std::cout << ans << "\n";
    for (int i = 0; i < n; ++ i) {
    	for (int j = 0; j < m; ++ j) {
    		std::cout << a[i][j] << " \n"[j == m - 1];
    	}
    }
}

D1. Candy Party (Easy Version)

题意:\(n\)个人每个人有一个糖果。每个人必须恰好给一个人\(2\)的幂的糖果,并且恰好被一个人给糖果。要使得最终每个人的糖果数相等。

每个人要给出的糖果和接受的糖果数是固定的,记录下来看给出的和收入的\(2\)的幂的数量是不是一样。注意特判有一个人不过怎么凑的凑不出平均数的情况。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<i64> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    i64 sum = std::accumulate(a.begin(), a.end(), 0ll);
    if (sum % n) {
    	std::cout << "NO\n";
    	return;
    }

    i64 x = sum / n;
    std::vector<int> cnt1(30), cnt2(30);
    for (int i = 0; i < n; ++ i) {
    	if (a[i] == x) {
    		continue;
    	}
    	bool flag = false;
    	for (i64 j = 0; j < 30; ++ j) {
    		for (i64 k = 0; k < 30; ++ k) {
    			if (a[i] + (1ll << j) - (1ll << k) == x) {
    				++ cnt1[j];
    				++ cnt2[k];
    				flag = true;
    			}
    		}
    	}

    	if (!flag) {
    		std::cout << "NO\n";
    		return;
    	}
    }

    for (int i = 0; i < 30; ++ i) {
    	if (cnt1[i] != cnt2[i]) {
    		std::cout << "NO\n";
    		return;
    	}
    }

    std::cout << "YES\n";
}

D2. Candy Party (Hard Version)

题意:跟\(D1\)一样,不过每个人可以选择给或不给。

发现如果这个人只给或者只接受,那么它和平均数的差是\(2\)的幂。例如\(a_i > x\),则可以表示为\(+2^i, - 0\),或者\(+2^{i+1}, -2^i\)。我们需要考虑应该选择哪一种。
发现这个只和两位有关,那么我们从高往低考虑,如果当前位的给出和收入不为0,就可以把\(+2^i, -0\)的变成\(+2^{i+1}, -2^i\)

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<i64> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    i64 sum = std::accumulate(a.begin(), a.end(), 0ll);
    if (sum % n) {
    	std::cout << "NO\n";
    	return;
    }


    i64 x = sum / n;
    std::vector<int> cnt1(30), cnt2(30);
   	std::vector<int> sum1(30), sum2(30);
    for (int i = 0; i < n; ++ i) {
    	if (a[i] == x) {
    		continue;
    	}

    	bool flag = false;
    	for (i64 j = 0; j < 30; ++ j) {
    		if (a[i] - (1ll << j) == x) {
    			++ sum1[j];
    			flag = true;
    			break;
    		}
    	}

    	for (i64 j = 0; j < 30; ++ j) {
    		if (a[i] + (1ll << j) == x) {
    			++ sum2[j];
    			flag = true;
    			break;
    		}
    	}

    	if (flag) {
    		continue;
    	}

    	flag = false;
    	for (i64 j = 0; j < 30; ++ j) {
    		for (i64 k = 0; k < 30; ++ k) {
    			if (a[i] + (1ll << j) - (1ll << k) == x) {
    				++ cnt1[k];
    				++ cnt2[j];
    				flag = true;
    			}
    		}
    	}

    	if (!flag) {
    		std::cout << "NO\n";
    		return;
    	}
    }

    for (int i = 29; i ; -- i) {
    	int d = (cnt1[i] - cnt2[i] + sum1[i] - sum2[i]);
    	if (d > 0) {
    		if (sum2[i - 1] < d) {
    			std::cout << "NO\n";
    			return;
    		}

    		sum2[i - 1] -= d;
    		cnt1[i - 1] += d;
    	} else if (d < 0) {
    		d = -d;
    		if (sum1[i - 1] < d) {
    			std::cout << "NO\n";
    			return;
    		}

    		sum1[i - 1] -= d;
    		cnt2[i - 1] += d;
    	}
    }

    if (cnt1[0] - cnt2[0] + sum1[0] - sum2[0] != 0) {
    	std::cout << "NO\n";
    	return;
    }

    std::cout << "YES\n";
}
posted @ 2025-02-27 18:59  maburb  阅读(28)  评论(0)    收藏  举报