牛客小白月赛110(打表、容斥+二分、区间dp、DFS)

牛客小白月赛110(打表、容斥+二分、区间dp、DFS)

A. 智乃办赛

思路

首先, n − 1 n-1 n1 ,把 [ 1 , 500 ] 、 [ 501 , 1000 ] . . . [1, 500]、[501, 1000] ... [1,500][501,1000]... 映射到 [ 0 , 499 ] 、 [ 500 , 999 ] . . . [0, 499]、[500, 999] ... [0,499][500,999]...

字母 = ( n − 1 ) / 500 + ′ A ′ (n-1) / 500 + 'A' (n1)/500+A ( n − 1 ) / 500 (n-1) / 500 (n1)/500 得到第几段; + ′ A ’ + 'A’ +A 得到对应字母的 a s c l l ascll ascll

数字 = ( n − 1 ) % 500 + 1 (n-1) \% 500 + 1 (n1)%500+1 % 500 \%500 %500 得到余数,即在段内是第几个; + 1 + 1 +1 映射到原区间。

code

#include<bits/stdc++.h>

using namespace std;


int main(){
	
	int n;
    cin >> n;
    int x = (n-1) / 500;
    int y = (n-1) % 500 + 1;
    
    printf("%c%03d\n", x+'A', y);	// %03d 三位整型,不足补0
	
	return 0;
}

B. 智乃的Wordle

思路

先统计每个字符出现的情况,再根据题意判断即可。

code

#include<bits/stdc++.h>

using namespace std;

int v[500];

int main(){
	
	string s, t;
    cin >> s >> t;
    
    for(int i = 0; i < 8; i++) v[s[i]]++;
    
    for(int i = 0; i < 8; i++){
        if(s[i] == t[i]) cout << "g";
        else if(v[t[i]] != 0) cout << "y";
        else cout << "r";
    }
    cout << endl;
    if(s == t) cout << "congratulations" << endl;
    else cout << "defeat" << endl;
	
	return 0;
}

C. 智乃的数字(打表、容斥+二分)

思路

看数据范围,1e5 次查询,每次查询 k 在 1e9。即,在O(log) 或O(1) 的时间复杂度内解决每次查询。

综上,大概率有公式或规律。


打表可以发现,每隔 7 个元素,+30。

求出前7个元素,判断是第几个循环节即可。

完毕。


更深入的思考:

  1. 奇数,即,不是 2 的倍数
  2. 以 5 结尾,即,是 5 的倍数,不是 2 的倍数
  3. 数位和是 3 的倍数,即,是 3 的倍数

三个条件互相有交叉,考虑容斥原理,如下图所示:

在这里插入图片描述

(图片来自官方题解)

对于1 到 x,阴影部分满足题意的元素个数为: x / 5 + x / 3 − x / 15 − x / 6 − x / 10 + x / 30 x / 5 + x / 3 - x / 15 - x / 6 - x / 10 + x / 30 x/5+x/3x/15x/6x/10+x/30

理解为计算阴影部分的面积,x / 5 表示 5 的倍数所在圆的面积,x / 3 表示 3 的倍数所在圆的面积,x / 15 表示 5 的倍数与 3 的倍数相交的部分面积,x / 6表示 2 的倍数与 3 的倍数相交的部分面积…

显然,对于区间[1, x],x 越大,满足题意的元素个数越多。

二分找到最小的x,使得区间[1, x]中,有 k 个满足题意的元素。

code

打表:

#include<bits/stdc++.h>

using namespace std;

long long a[1005];
bool check(int x){
	if(x % 2 == 0) return false;
	if(x % 5 == 0) return true;	
	int sum = 0;
	while(x) sum += x % 10, x /= 10;
	return sum % 3 == 0;
}

int main(){
	
	int cnt = 0; 
	for(int i = 1; i <= 1000; i++){
		if(check(i)){
            a[cnt++] = i;
            // cout << a[cnt] << " "; // 打表用
		} 
	}	
              
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++){
    	long long x;
    	cin >> x;
        x--;
    	cout << a[x%7] + x / 7 * 30 << endl;
	}
	 
	
	return 0;
}

二分:

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

const int maxn = 1e6 + 5;

ll count(ll x){
	return x / 5 + x / 3 - x / 15 - x / 6 - x / 10 + x / 30;
}

int main(){
	
	int ncase;
	cin >> ncase;
	
	while(ncase--){
		ll k;
		cin >> k;
		ll l = 1, r = 1e18, res;
		while(l <= r){
			ll mid = l + r >> 1;
			if(count(mid) >= k){
				res = mid;
				r = mid - 1;
			}
			else l = mid + 1;
		}
		cout << res << endl;
	}
	
	return 0;
}

D. 智乃与长短期主义者博弈(区间dp or 记忆化DFS)

思路

根据题意:

  1. 短期主义者的行为表示为:对于区间 [ l , r ] [l, r] [l,r],每次选取 m a x ( a [ l ] , a [ r ] ) max(a[l], a[r]) max(a[l],a[r])
  2. 长期主义者的行为表示为:设 h ( l , r ) h(l, r)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值