dengch

 

atcoder321

A

A - 321-like Checker
题目概述:给定一个数,若它的第i位都大于第i+1位,我们就把它称为321数,判断给定的数是否为321数
解题思路:直接模拟即可

点击查看代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
#include <vector>
#include <map>
#include <set>

using namespace std;

typedef long long LL;
typedef pair<int,int>PII;

int main(){
	string str;
	cin >> str;

	bool frag = true;
	for(int i = 0; i < str.size() - 1; i ++){
		if(str[i] <= str[i + 1]){
			frag = false;
			break;
		}
	}

	if(frag)puts("Yes");
	else puts("No");

	return 0;
}

B

B - Cutoff
题目概述:有n轮比赛,每轮比赛会获得一个分数,最后的总分数为去除最小值和最大值的和。现在给定前n轮的分数,问第n轮至少需要获得多少分数,才能使最后总分>=X.
解题思路:由于最后一轮的分数只会影响当前的最大值和最小值。我们先计算当前去掉最小值和最大值的和,再用得到的和与X作比较,如果它们之间的差值小于等于当前最小值,那么最后一轮的分数最小为0;如果差值大于当前最大值,那么无解;否则,最后一轮的分数就等于该差值。

点击查看代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
#include <vector>
#include <map>
#include <set>

using namespace std;

typedef long long LL;
typedef pair<int,int>PII;
const int N = 110;

int num[N];

//深入学习以下substr()函数用法
int main(){
	int n,x;
	cin >> n >> x;

	for(int i = 0; i < n - 1; i ++)cin >> num[i];

	sort(num,num + n - 1);

	int sum = 0;
	for(int i = 1; i < n - 2; i ++)sum += num[i];

	if(x - sum <= num[0])cout << 0 << endl;
	else if(x - sum > num[n - 2])cout << -1 << endl;
	else cout << x - sum << endl;

	return 0;
}

C

C - 321-like Searcher
题目概述:要求我们输出第K小的321数
解题思路:由于321数其实不是很多(1022个数),可以直接暴力打表枚举出所有321数,再排序即可。

点击查看代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
#include <vector>
#include <map>
#include <set>
#include <queue>

using namespace std;

typedef long long LL;
typedef pair<int,int>PII;
const int INF = 1e9;
vector<LL>res,all;


int main(){

	int k;
	cin >> k;

	res.push_back(9876543210);
	res.push_back(987654321),res.push_back(987654320),res.push_back(987654310);
	res.push_back(987654210),res.push_back(987653210),res.push_back(987643210);
	res.push_back(986543210),res.push_back(976543210),res.push_back(876543210);
	res.push_back(987543210);
	//枚举9位数的321数,需要预先处理出来再记录到数组中
	// for(int i = 987654321; i >= 876543210; i --){
	// 	string str = to_string(i);
	// 	bool frag = true;
	// 	for(int j = 0; j < str.size() - 1; j ++){
	// 		if(str[j] <= str[j + 1]){
	// 			frag = false;
	// 			break;
	// 		}
	// 	}
	// 	if(frag)cout << i << endl;
	// }

	//枚举8位数的321数
	for(int i = 98765432; i >= 76543210; i --){
		string str = to_string(i);
		bool frag = true;
		for(int j = 0; j < str.size() - 1; j ++){
			if(str[j] <= str[j + 1]){
				frag = false;
				break;
			}
		}
		if(frag)res.push_back((LL)i);
	}
	//枚举7位数的321数
	for(int i = 9876543; i >= 6543210; i --){
		string str = to_string(i);
		bool frag = true;
		for(int j = 0; j < str.size() - 1; j ++){
			if(str[j] <= str[j + 1]){
				frag = false;
				break;
			}
		}
		if(frag)res.push_back((LL)i);
	}
	//枚举剩余的321数
	for(int i = 1; i <= 987654; i ++){
		string str = to_string(i);
		bool frag = true;
		for(int j = 0; j < str.size() - 1; j ++){
			if(str[j] <= str[j + 1]){
				frag = false;
				break;
			}
		}
		if(frag)res.push_back((LL)i);
	}
	res.push_back(0);
	sort(res.begin(),res.end());
	cout << res[k] << endl;

	return 0;
}

补充解法:枚举每一个数(0-9)选或不选

点击查看代码
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int main() {
    vector<long long> all;
    for(int i = 1; i < 1024; i++) {
        long long val = 0;
        for(int j = 0; j < 10; j++) {
            if((i >> j) & 1) {
                val = val * 10 + (9 - j);
            }
        }
        all.push_back(val);
    }
    sort(all.begin(), all.end());
    int K;
    cin >> K;
    cout << all[K] << '\n';
}

D

D - Set Menu
题目概述:有n份主餐和m份副餐,每份主餐可以和任意一份副餐进行搭配。问所有搭配的总和是多少,当副餐和主餐的价格大于p时,其总价格取p
解题思路:显然如果最暴力的做法就是两重循环,但肯定会超时。我们可以枚举主餐(副餐也一样)ma_i,计算p-ma_i。使用二分在副餐价格中找到p-ma_i对应的位置pos,对于pos之前的价格,其和主餐价格之和不会超过p,所以这部分的总价格为ma_i * (pos - 1) + sspos-1;对于pos及之后的价格,其总和大于等于p,那么全部取p,总价格为(m - pos + 1) * p.值得注意的是,当序列中不存在我们要找的数时,二分返回的是这个数字应该在的坐标,那么这里就会有边界问题:当二分返回的pos为1或m时,我们不知道要找的这个数是等于最小值(最大值)还是小于最小值(最大值)。所以这里需要我们预先过滤一下。
时间复杂度:\(O(nlogm)\)

点击查看代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
#include <vector>
#include <map>
#include <set>
#include <queue>

using namespace std;

typedef long long LL;
typedef pair<int,int>PII;
const int N = 200010,INF = 0x3f3f3f3f;
int ma[N],s[N];
LL ss[N];
int n,m,p;

int find(int x){
	int l = 1,r = m;
	while(l < r){
		int mid = l + r >> 1;
		if(s[mid] >= x)r = mid;
		else l = mid + 1;
	}

	return l;
}

int main(){
	cin >> n >> m >> p;
	
	for(int i = 1; i <= n; i ++)cin >> ma[i];
	for(int i = 1; i <= m; i ++)cin >> s[i];
		
	sort(s + 1,s + m + 1);

	for(int i = 1; i <= m; i ++)ss[i] = (LL)ss[i - 1] + s[i];

	LL res = 0;
	for(int i = 1; i <= n; i ++){
		//过滤掉小于等于最小值或大于等于最大值的数
		if(p - ma[i] >= s[m]){
			res = (LL)res + ss[m] + (LL)m * ma[i];
		}else if(p - ma[i] <= s[1]){
			res = (LL)res + (LL)m * p;
		}else{
			int pos = find(p - ma[i]);
			res = (LL)res + (LL)(m - pos + 1) * p + (LL)ma[i] * (pos - 1) + ss[pos - 1]; 
		}
		
	}

	printf("%lld\n",res);

	return 0;
}

E

E - Complete Binary Tree
题目概述:给定一棵完全二叉树,编号规则为左儿子节点编号为父节点编号的2倍,右儿子节点编号为父节点编号的2倍加1.问距离节点x为k的点有多少个
解题思路:先考虑在节点x下的节点中有多少个节点满足条件,在考虑x节点上面的节点中满足条件的节点数

点击查看代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
#include <vector>
#include <map>
#include <set>
#include <cmath>
#include <queue>

//#define x first 
//#define  y second
using namespace std;

typedef long long LL;
typedef pair<LL,LL>PLL;
const int N = 200010;
//在最大节点编号为n的情况下,计算节点x以后的那些点中距离x为k的点的数目。
LL getcnt(LL x,LL k,LL n){
	if(x > n)return 0;
	if(k > 60)return 0;

	LL l = x,r = x;
	while(k){
		k --;
		l *= 2;
		if(l > n)return 0;
		r = min(n,2ll * r + 1ll);
	}

	return r - l + 1ll;

}

void solve(){
	LL n,x,k;
	scanf("%lld%lld%lld",&n,&x,&k);
	if(k == 0)puts("1");
	else{
		LL res = getcnt(x,k,n);
		int dis = 0;
		LL now = x;
		//计算x以前的那些点中距离x为k的点的数目
		while(now > 1){
			dis ++;
			if(dis == k){
				res++;
				break;
			}
			//now * 4 + 1 - last在二叉树(编号规则为左儿子为父节点*2,右儿子为父节点*2+1)
			//中可以回到当前节点的父节点的另一个子节点
			LL last = now;
			now /= 2;
			res += getcnt(now * 4 + 1 - last,k - dis - 1,n);
		}
		cout << res << endl;
	}

}


int main(){
	int T;
	cin >> T;

	while(T --){
		solve();
	}

	
	return 0;
}

F

F - #(subset sum = K) with Add and Erase
题目概述:有q次操作,每次操作要么加入一个数,要么移除一个数,针对每次操作,输出总和为k的方案数
解题思路:采用动态规划的思想。集合表示:dp[i]:和为i的方案数。集合划分:dp[i] = dp[i] + dp[i - x].
当加入一个数操作时,为了避免累加效应,我们倒着枚举。

点击查看代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
#include <vector>
#include <map>
#include <set>
#include <cmath>
#include <queue>

//#define x first 
//#define  y second
using namespace std;

typedef long long LL;
typedef pair<int,int>PII;
const int N = 5010,MOD = 998244353;
int dp[N];//dp[i]:和为i的方案数

int main(){
	int q,k;
	cin >> q >> k;

	dp[0] = 1;
	while(q --){
		string op;
		int x;
		cin >> op >> x;
		if(op == "+"){
			//操作只会对位于区间[x,k]中的数有影响,"+"操作需要倒序遍历,"-"操作正序遍历
			for(int i = k; i >= x; i --){
				dp[i] += dp[i - x];
				if(dp[i] >= MOD)dp[i] -= MOD;
			}
		}else{
			for(int i = x; i <= k; i ++){
				dp[i] -= dp[i - x];
				if(dp[i] < 0)dp[i] += MOD;
			}
		}
		cout << dp[k] << endl;
	}

	

	return 0;
}

posted on 2023-09-25 22:02  BkDench  阅读(26)  评论(0)    收藏  举报

导航