CQBZ周赛Round#20解题报告


1.观前提醒

本人码力极差,码风不好,不喜勿喷。

2.T1 粮仓 rice(CF 785C)

  CF 785C on Luoguicon-default.png?t=N7T8https://www.luogu.com.cn/problem/CF785CCF 785C on CQBZOJicon-default.png?t=N7T8https://43.139.55.23/contest/4546/problem/1

40pts code

首先贴上我的 40pts 代码:



#include<bits/stdc++.h>
#define int long long
int maxrice,dayrice,days,nowrice;
using namespace std;
signed main(){
	//freopen("rice.in","r",stdin);
	//freopen("rice.out","w",stdout);
	scanf("%lld%lld",&maxrice,&dayrice);
	nowrice=maxrice;
	for(int i=1;;i++){
		if(nowrice==maxrice)nowrice+=0;
		else if(maxrice-nowrice<dayrice)nowrice=maxrice;
		else nowrice+=dayrice;
		nowrice-=i;
		if(nowrice<=0){
			days++;break;
		}
		else days++;
	}
	cout<<days;
	return 0;
}

但是TLE了,因为数据范围最大到10^{18},所以暴力是肯定不行的。

Solution

对于前m天而言,由于每次调走的谷物都会在第二天补满,因此,这些天并不会造成谷物的损失,从第m+1天开始,谷物的总量才会减少。对于第m+i天而言,谷物总计减少i了个,第天往后谷物总共减少了\huge{\frac {(i+1)\times i}{2}},因为第m+1天开始,总量变为了n-m,所以,只需要求解n-m \le \frac{(i+1)\times i}{2}中的i的最小值即可。

注意:在计算过程中,需要先判断nm的相对大小,如果n小于等于m,直接输出n即可。

AC code


#include<bits/stdc++.h>
using namespace std;
long long n,m,ans;
int main(){
	//freopen("rice.in","r",stdin);
	//freopen("rice.out","w",stdout);
	cin>>n>>m;
	if(m>=n){
	    cout<<n;
	    return 0;
	}
	long long s=n-m-1,s1=0;
	if(s>1000006280){
		s1=1000006280;
		ans=44720;
		for(long long i=44722;s1<s;i++){
			s1+=i;
			ans++;
			if(s1>=s)break;
		}
		cout<<ans+m+1;
		return 0;
	}
	for(long long i=2;s1<s;i++){
		s1+=i;
		ans++;
		if(s1>=s)break;
	}
	cout<<ans+m+1;
	return 0;
}

3.T2 盒子与球 ball(Atcoder ABC214E)

AT_abc214_e on Luoguicon-default.png?t=N7T8https://www.luogu.com.cn/problem/AT_abc214_eAT_abc214_e onCQBZOJicon-default.png?t=N7T8https://43.139.55.23/contest/4546/problem/2

Solution

因为每个球能够放在一个指定的区间范围内中的任意一个位置,这里假设按照从左往右的顺序来进行插入小球。对于每一个小球而言,为了使得让尽可能多的后续的小球能够放到指定位置上,因此,我们就需要将这个小球放在尽可能靠左的位置。

因此,这里利用贪心的思想,先对所有的球能够放的范围按照左端点从小到大进行排序,再把排序后的内容按照右端点大小设置一个优先队列。每次计算时,从小到大枚举左端点l,然后,将所有新的l符合要求的小球全部放入优先队列中,再然后,pop出一个小球出来放在当前l的位置。此时,判断,当前退出的这个r最小的球是否大于等于当前l,如果大于等于,则说明可以放,则继续枚举下一个l,否则,退出循环并输出No。当所有小球全部计算完都符合要求后,则输出Yes

注意:别忘了要计算最后一个小球。

AC code

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
struct Sec {
    int l, r;
} a[N];
int n;
int main () {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int T; cin >> T;
    while (T--) {
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i].l >> a[i].r;
        stable_sort(a+1, a+n+1, [&](Sec p, Sec q){ return p.l ^ q.l ? p.l < q.l : p.r < q.r; });
        priority_queue<int, vector<int>, greater<int>> q;
        int l, r = 0; bool ans = 1;
        while (r < n || !q.empty()) {
            if (q.empty())
                l = a[++r].l,
                q.push(a[r].r);
            while (r < n && a[r+1].l <= l)
                q.push(a[++r].r);
            if (q.top() < l) { ans = 0; break; }
            q.pop(), l++;
        }
        cout << (ans ? "Yes" : "No") << '\n';
    }
    return 0;
}

4.T3 兼容数字 compat(CF 165E)

CF165E on Luoguicon-default.png?t=N7T8https://www.luogu.com.cn/problem/CF165ECF165E on CQBZOJicon-default.png?t=N7T8https://43.139.55.23/contest/4546/problem/3

Solution

对于每一对 a_i&a_j=0,若 a_i与 的二进制表示中有同一位均为1 ,那么 a_i& a_j的这一位也会为1 。因此可以发现,a_ja_i按位取反后的子集。

50pts

对于每一个询问,枚举a_i按位取反的数字的子集,判断数列中是否存在某个子集。

最差情况下复杂度:O(n \times V)(V=2^k-1)

100pts
  1. 枚举子集的过程中,涉及很多重复计算,小小的加一个记忆化,dfs找可行子集即可搞定。

  2. 如果枚举a_i按位取反的数字为x ,那么任意 x的子集都能作为备选答案。那么可以从小到大预处理每个数字是否有子集在数列中存在。因为输出任意一个数即可,所以我们可以看成求满足 a \cap c的最大数,即\max_{a\bigcap c} a,这个使用高维前缀和维护即可。

复杂度:O(n+V\log V)

AC code

#include <bits/stdc++.h>
#pragma GCC optimize(2)
#define int long long
using namespace std;
const int N = 1e6 + 5;
const int logn = 22;
int n, a[N], f[1 << logn];
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(nullptr), cout.tie(nullptr);
	cin >> n;
	for(int i = 1; i <= n; i++) 
		cin >> a[i], f[a[i]] = a[i];
	for(int i = 0; i < logn; i++) {
		for(int j = 0; j < (1 << logn); j++) {
			if((j & (1 << i)) && f[j ^ (1 << i)]) f[j] = f[j ^ (1 << i)];
		}
	}
	for(int i = 1; i <= n; i++) {
		int XOR = ((1 << logn) - 1) ^ a[i];
		cout << (f[XOR] ? f[XOR] : -1) << ' ';
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值