Codeforces Round #697 (Div. 3)补题

Codeforces Round #697 (Div. 3)补题

昨天晚上看到不计rating,加之评测机非常卡就没有再打了,今天把剩下三个题补了。


D. Cleaning the Phone

\(~~~~\)晚上做的时候想复杂了,其实所有的convenience points只有两个值1和2,所以可以根据convenience points把所有数分类到a,b两个数组里,再分别记录两个数组里所选元素的和\(suma\)\(sumb\)。问题变成了在a中选\(x\)个元素,b中选\(y\)个元素,使所选元素的和大于m,为了\(x+2y\)尽可能小要先把两个数组从大到小排序。

\(~~~~\)使用双指针的做法,开始时x为0,y为\(b.size()\),x从0到\(a.size()\)进行遍历,每次while(y>0 && suma+sumb-b[y-1]>=m){sumb -= b[--y]}来更新y的值,之后令\(suma\)加上\(a[x]\)并更新答案。遍历完毕后就相当于枚举了所有\(suma+sumb\geq m\)的情况。

  • 代码

#define LOCAL0
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define pb(x) push_back(x) 
#define ALL(x) x.begin(), x.end()
#define RALL(x) x.rbegin(), x.rend()
#define sz(x) (int)x.size()
using namespace std;
typedef long long ll;
int main()
{
	#ifdef LOCAL
		freopen("in.txt", "r", stdin);
    	freopen("out.txt", "w", stdout);
	#endif
	int T = 1;
	cin >> T;
	while(T--){
		vector<int> a, b;
		int n;
		ll m;
		cin >> n >> m;
		vector<int> all(n);
		for(auto &x:all){
			cin >> x;
		}
		int tag;
		for(auto x:all){
			cin >> tag;
			if(tag==1){
				a.pb(x);
			}
			else{
				b.pb(x);
			}
		}
		sort(RALL(a));
		sort(RALL(b));
		int ans = INT_MAX;
		ll suma = 0, sumb = accumulate(ALL(b), 0ll);
		int l = 0, r = b.size();
		for(l=0; l<=sz(a); l++){
			while(r>0 && suma+sumb-b[r-1]>=m){
				sumb -= b[--r];
			}
			if(suma+sumb>=m){
				ans = min(ans, 2*r+l);
			}
			if(l<sz(a)){
				suma += a[l];
			}
		}
		if(ans == INT_MAX) cout << "-1\n";
		else cout << ans << endl;
	}
	return 0;
}
 

F. Unusual Matrix

\(~~~~\)题意是给一个只含01的方矩阵,每次可以使一行或一列的数字翻转,问能否通过若干次这种操作把原矩阵转换成另一个矩阵。

\(~~~~\)首先一行或一列最多翻转一次,因为翻两次就变回去了。其次如果知道了要执行哪些操作,这些操作执行的顺序对结果没有影响。

\(~~~~\)那么对于第一行的操作,只有两种情况:翻转第一行的数字,如果翻转后还有不一样的数字,就翻转那些数字所在的列;或者直接翻转不一样数字所在的列。这两种情况无论用哪种,都能使第一行与目标矩阵第一行相等,并且之后的操作都是确定的,因为第2到n行如果有不一样的数字只能翻转当前行,如果再翻转某一列的话就会破坏第一行已经构造好的数字,所以第2到n行中的某一行如果翻转后还是不能与目标矩阵相等,那么就无法构造出来。所以讨论一下第一行的两种情况中有没有符合条件的构造。

\(~~~~\)实际上进一步想的话,这两种情况任选一种就行了,不需要讨论。因为第一行的这两种操作所翻转的列正好是互补的,如果能够造出来,两种情况下对2到n行每一行的操作正好是相反的,所以一种情况能够造出来,另一种情况就也可以。

  • 代码
#define LOCAL0
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define FOR(i, a, b) for (int i=a; i<(b); i++)
#define pb(x) push_back(x) 
#define ALL(x) x.begin(), x.end()
#define RALL(x) x.rbegin(), x.rend()
#define sz(x) (int)x.size()
using namespace std;
typedef long long ll;
string a[1005], b[1005];
int main()
{
	#ifdef LOCAL
		freopen("in.txt", "r", stdin);
    	freopen("out.txt", "w", stdout);
	#endif
	int T = 1;
	cin >> T;
	while(T--){
		int n;
		cin >> n;
		FOR(i, 0, n) cin >> a[i];
		FOR(i, 0, n) cin >> b[i];
		FOR(i, 0, n){
			if(a[0][i]!=b[0][i]){
				FOR(j, 0, n){
					a[j][i] = a[j][i]=='0'?'1':'0';
				}
			}
		}
		bool flag = true;
		FOR(i, 1, n){
			if(a[i]!=b[i]){
				FOR(j, 0, n){
					a[i][j] = a[i][j]=='0'?'1':'0';
				}
				if(a[i]!=b[i]){
					flag = false;
					break;
				}
			}
		}
		if(flag) cout << "YES\n";
		else cout << "NO\n";
	}
	return 0;
}
 

G. Strange Beauty

\(~~~~\)这道题符合条件的一组数如果从小到大排列的话,满足每一个数都是下一个数的因数,所以题意就是寻找有这样的关系的最长的一组数。\(dp[x]\)表示以\(x\)为结尾,符合上述条件的最长的一组数(从小到大排列),用\(cnt[x]\)表示题目所给数组中\(x\)的个数,可以得到转移方程\(dp[x]=cnt[x]+\max_{x\%y=0}dp[y]\)。状态转移可以用类似埃氏筛的做法,计算完\(dp[x]\)以后,对所有\(x\)的倍数进行更新。

  • 代码
#define LOCAL
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define debug(msg) cout << (msg) << endl
#define FOR(i, a, b) for (int i=a; i<(b); i++)
#define pb(x) push_back(x) 
#define ALL(x) x.begin(), x.end()
#define RALL(x) x.rbegin(), x.rend()
#define sz(x) (int)x.size()
using namespace std;
typedef long long ll;
int main()
{
	#ifdef LOCAL
		freopen("in.txt", "r", stdin);
    	freopen("out.txt", "w", stdout);
	#endif
	int T = 1;
	cin >> T;
	while(T--){
		int mx = 0, n, x;
		cin >> n;
		map<int, int> mp;
		FOR(i, 0, n){
			cin >> x;
			mp[x]++;
			mx = max(mx, x);
		}
		vector<int> dp(mx+1);
		int ans = 0;
		FOR(i, 1, mx+1){
			dp[i] += mp[i];
			for(int j=2*i; j<=mx; j+=i){
				dp[j] = max(dp[j], dp[i]);
			}
			ans = max(ans, dp[i]);
		}
		cout << n-ans << endl;
	}
	return 0;
}
posted @ 2021-01-26 16:06  DinoMax  阅读(98)  评论(0)    收藏  举报