2022山大“地炜杯”补题

目录

A、Seventeen

E、Subsegments

H、Counting

K、Coins


A、Seventeen

题意:在给定的 1 - n 的数使用这 n 个数通过运算符  * 、 + 、 -  和()来进行组合,使得最后式子的结果最终是17,(最后的式子可以不用按照顺序进行组合)

思路:给出的例子是 输入: 10  输出 : 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 - 9 - 10 

最后的结果是17, 本身以为是一个bfs的题,发现不是很容易有递归,之后发现是一个构造题,当输入 n <= 3 的时候无论如何组合都无法凑出17,所以n <= 3 的情况是无解的情况,在组合式中 + 号和 - 号可以起到相互抵消的作用,每四个数可以抵消,i + (i  - 3) == (i - 2) + (i - 1),所以可以将前8中按照自定的顺序构造出来,之后的值可以用其抵消(这里说的比较笼统,具体看代码就行)

代码:

#include <bits/stdc++.h>
using namespace std;

int n;
const int N = 100;
string str[N];

void solve() {
	str[1] = "-1";
	str[2] = "-1";
	str[3] = "-1";
	str[4] = "(4+1)*3+2";
	str[5] = "3*5+1*(4-2)";
	str[6] = "1-2+3+4+5+6";
	str[7] = "2+3+4+7+(6-5)*1";
	str[8] = "1+3+4+6+(5-2)*(8-7)";
	
	cin >> n;
	if(n < 4) {
		cout << str[n] << "\n";
	}
	else {
		while(n > 8) {
			cout << n << "+" << n - 3 << "-" << n - 2 << "-" << n - 1 << "+";
			n -= 4; 
		}
		
		cout << str[n] << "\n";
	}
}



int main() {
	solve();
	
	
	return 0;
}

E、Subsegments

题意:给予一个数组,寻找区间中有多少个区间【l ,r】满足(l <= i <= r) a[ l ] * a[ l + 1 ] * .. * a[ r - 1 ] * a[ r ] 对998,244,353取余结果是x(x是自行输入的)

思路:可以使用类似于滑动窗口的方法(维护前缀积同样可以) ,由于是乘积,所以需要处理x == 0的情况,

1、当x == 0的时候,区间中至少需要出现一个0,所以可以以0为边界进行计算

//当x == 0的时候单独处理
void solve() {
	int last = 0;
	
    //当出现a[i] = 0的时候可以以这个点为基点想两侧扩张,由于是按照顺序枚举的所有的a[i] = 0
    //所以要避免重复的情况出现 方法:记录上一个0出现的位置,就可以避免重复类型的出现
	for (int i = 1; i <= n; i ++ ) {
		if(a[i] == 0) {
			ans += (i - last) * (n - i + 1);
			last = i;
		}
	} 
	printf("%lld\n", ans);
}

2、k != 0 时,区间中是一定不能存在0的,所以完整的自区间被0分割,代码中使用了一种比较奇特的方法进行模拟,学习一下

代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 5e5 + 5;
const ll mod = 998244353;
ll a[N];
unordered_map<int, int> mp;
ll n, m, ans;

void solve() {
	int last = 0;
	
	for (int i = 1; i <= n; i ++ ) {
		if(a[i] == 0) {
			ans += (i - last) * (n - i + 1);
			last = i;
		}
	} 
	printf("%lld\n", ans);
}

int main() {
	scanf("%lld%lld", &n, &m);
	
	for (int i = 1; i <= n; i ++ ) {
		scanf("%lld", &a[i]);
	}
	
	if(m == 0) {
		solve();
		return 0;
	}
	
	int temp = 1;
	mp[m] ++ ;
	//类似滑动窗口 
	for (int i = 1; i <= n; i ++ ) {
		temp = (temp * a[i]) % mod;
		if(temp == 0) {
			temp = 1;
			mp.clear();
			mp[m] ++ ;
			continue;
		} 
		if(mp.count(temp)) ans += mp[temp];
        //每次记录该值的m的倍数,当下一次出现该值时,可以直接进行访问得到
		mp[(temp * m) % mod] ++ ;
	}
	
	printf("%lld\n", ans);
	
	
	return 0;
} 

H、Counting

题意:一共有k个人,并给出每个人在每一秒是的移动方向,输出在每一秒有对人可以处于同一个格子

思路:通过方向进行枚举即可

#include <bits/stdc++.h>
using namespace std;

const int N = 2e3 + 5;
struct node{
    int x, y;
}s[N];
string str[N];
 int n, m, k, t;

int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};

int st[N][N];


void solve() {
    for (int i = 0; i <= t; i ++ ) {
        int ans = 0;
        
        //采用一种更加省事的标记方法
        for (int j = 0; j < k; j ++ ) {
        	ans += st[s[j].x][s[j].y];
            
        	st[s[j].x][s[j].y] ++ ;
		}
        
        printf("%d\n", ans);
        
        for (int j = 0; j < k; j ++ ) {
            int flag;
            st[s[j].x][s[j].y] -- ;
            //人已经在这种状态下移动走了,所以需要在这个位置减去这个人
            if(str[j][i] == 'U') flag = 0;
            if(str[j][i] == 'R') flag = 1;
            if(str[j][i] == 'D') flag = 2;
            if(str[j][i] == 'L') flag = 3;
            
            s[j].x += dx[flag];
            s[j].y += dy[flag];
        }
    }
}

int main() {
   
    scanf("%d%d%d%d", &n, &m, &k, &t);
    
    for (int i = 0; i < k; i ++ ) {
        scanf("%d%d", &s[i].x, &s[i].y);
    }
    for (int i = 0; i < k; i ++ ) { 
        cin >> str[i];
    }
    
    solve();
    
    
    
    
    return 0;
}

K、 Coins

题意;Alice 有4种硬币分别是2、 3、 17、19,Bob  也有4种硬币分别是 5、 7、 11、13,,并且每个人对于每一种硬币,都有无限个的使用,对于给定的一个数谁可以使用他自己手中的硬币拼凑出来并且所需的硬币的数目最少

思路:由于Alice有2 和 3硬币,所以除了1Alice都可以使用硬币拼凑出,但是Bob手中的硬币就有了限制,可能无法拼出一些数,当所拼的数越大的时候Alice更占优势,所以会存在一个数,满足之后的一定都是Alice胜,这个数可以是 17 * 19 ,可以通过打表来找,也可以推断,由于Alice有17 和 19 两者互质,所以之后肯定是Alice用的最少,前面的可以使用线性dp的方法把每个人对于每个值组成所使用的的最少硬币的数目记录下来,最后在进行比较

#include <bits/stdc++.h>
using namespace std;

#define inf 0x3f3f3f3f
const int N = 2e6 + 7;
int a[] = {2, 3, 17, 19};
int b[] = {5, 7, 11, 13};

int fa[N], fb[N];

int main() {	
	int q, x;
	cin >> q;
	
	memset(fa, inf, sizeof fa);
	memset(fb, inf, sizeof fb);
	
	fa[0] = fb[0] = 0;
	
	for (int i = 0; i < 4; i ++ ) {
		for (int j = 0; j < 10000; j ++ ) {
			if(j >= a[i]) fa[j] = min(fa[j], fa[j - a[i]] + 1);
			if(j >= b[i]) fb[j] = min(fb[j], fb[j - b[i]] + 1);
		}
	}
	
	while (q -- ) {
		cin >> x;
		if(x >= 1000) {//肯定B用的多 
			cout << "A\n";
		}
		else if(fa[x] == inf && fb[x] == inf) cout << "-1\n";
		else if(fa[x] == inf) cout << "B\n";
		else if(fb[x] == inf) cout << "A\n";
		else if(fa[x] == fb[x]) cout << "both\n";
		else if(fa[x] < fb[x]) cout << "A\n";
		else cout << "B\n";
	}
	
	
	
	return 0;
}

之前我并没有参加这次的省赛,是在最近一次训练赛用了省赛的题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值