Codeforces Round 900 Div.3 前五题补题报告

得分情况

A题AC
B题AC
C题全部TLE
D题过了前9组,剩下的TLE
E题过了前11组,剩下的TLE

补题情况

全部AC

错题分析

C题

题目大意

有 t 组数据。每组数据给定 n,k,x。请你判断能否在 1∼n 中不重复的恰好选出 k 个数使得这 k 的数的和为 x。可以选出输出 YES,否则输出 NO。

初次思路

暴力枚举1~n区间中选出k个数的所有可能,如果有和为x的就输出 YES,否则输出 NO。(时间复杂度太高,样例都TLE)

正解思路

x x x小于等于最大可以凑出的数 ( n − k + 1 + n ) ∗ k / 2 (n-k+1+n)*k/2 (nk+1+n)k/2并且大于等于最小可以凑出的数 ( 1 + k ) ∗ k / 2 (1+k)*k/2 (1+k)k/2
必定有解!
因为这个区间的公差为1,1能凑出所有数,因此能凑出x,想凑出x+1,只需要将k个数中的其中一个加1就可以了(x+1必须在上述区间内)

正解代码

#include<iostream>
#include<cstdio>
#include<map>
using namespace std;
int main(){
	int t;
	cin>>t;
	while(t--){
		long long n,k,x;
		cin>>n>>k>>x;
		if(x>(n-k+1+n)*k/2 || x<(1+k)*k/2){
			cout<<"NO\n";
		}
		else{
			cout<<"YES\n";
		}
	}
	return 0;
}

错误原因

做题时没有对利用数学构造进行考虑。

D题

题目大意

给你一个长度为 的字符串 ,其中包含小写拉丁字母。
接下来会给你一个正整数 和两个长度为 的数组 和 。
保证这两个数组满足以下条件:
l 1 = 1 l_1=1 l1=1
r k = n r_k=n rk=n
l i < = r i l_i<=r_i li<=ri , 对于每个正整数 i, 1 < = i < = k 1<=i<=k 1<=i<=k
l i = r j + 1 ( j = i − 1 ) l_i=r_j+1(j=i-1) li=rj+1(j=i1), 对于每个正整数 i, 2 < = i < = k 2<=i<=k 2<=i<=k
现在给你一个正整数 q,表示你需要对 s 进行修改的次数。
每个修改都用一个正整数 x 来定义:
找出一个索引 i,使得 l i < = x < = r i l_i<=x<=r_i li<=x<=ri (注意这样的 是唯一的)。
a = m i n ( x , r i + l i − x ) a=min(x,r_i+l_i-x) a=min(x,ri+lix) b = m a x ( x , r i + l i − x ) b=max(x,r_i+l_i-x) b=max(x,ri+lix)
将 s 的子串从索引 a 反转到索引 b。

初次思路

暴力枚举出i,计算出a和b,翻转相应区间。

正解思路

利用差分标记元素被反转的次数,奇数次有改变,偶数次无改变,最后把奇数次的元素互换,输出字符串。

正解代码

#include<iostream>
#include<string>
#include<cstring>
#include<map>
using namespace std;
const int N=2e5+5;
int l[N],r[N];
map<int,int> mp;
int d[N];
void xht(){
	memset(l,0,sizeof l);
	memset(r,0,sizeof r);
	memset(d,0,sizeof d);
	mp.clear();
	string s;
	int n,k;
	cin>>n>>k;
	cin>>s;
	for(int i=1;i<=k;i++){
		cin>>l[i];
	}
	for(int i=1;i<=k;i++){
		cin>>r[i];
		for(int j=l[i];j<=r[i];j++){
			mp[j]=i;
		}
	}
	int q;
	cin>>q;
	while(q--){
		int x;
		cin>>x;
		d[min(x,l[mp[x]]+r[mp[x]]-x)]++;
		d[max(x,l[mp[x]]+r[mp[x]]-x)+1]--;
	}
	for(int i=1;i<=n;i++){
		d[i]+=d[i-1];
	}
	for(int i=1;i<=k;i++){
		for(int j=l[i];j<=l[i]+r[i]>>1;j++){
			if(d[j]%2==1){
				swap(s[j-1],s[l[i]+r[i]-j-1]);
			}
		}
	}
	cout<<s<<"\n";
}
int main(){
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		xht(); 
	}
	return 0;
}

E题

(CF为了多组输入竟然把时间设定为5秒)

题目大意

有 t 组数据。每组数据给定长度为 n 的数组 a 和 q 次询问。我们定义 f(l,r)(1≤l≤r≤n) 表示 a l a_l al&…& a r a_r ar 的结果。其中,& 表示位与运算。对于每次询问,将给定 l,k。请你找到最大的 r 使得 f(l,r)≥k。如果无解,输出 -1。

初次思路

每轮预处理l~n的&和,循环枚举出r

正解思路

N&N=N,所以&是可重复贡献问题,用ST表预处理,a&b<=a,所以&和是递减的,可以用二分找r

正解代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int ans[200005],st[200005][35],n,cnt=0;
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		cnt=0;
		int q; 
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			scanf("%d",&st[i][0]);
		}
		for(int j=1;j<=log2(n);j++){
			for(int i=1;i+(1<<j)-1<=n;i++){
				st[i][j]=st[i][j-1]&st[i+(1<<(j-1))][j-1];
			}
		}
		scanf("%d",&q);
		while(q--){
			int k,ll;
			scanf("%d %d",&ll,&k);
			if(st[ll][0]<k){
				ans[++cnt]=-1;
			}
			else{
				int l=ll,r=n;
				while(l<r){
					int mid=(l+r+1)>>1;
					int s=log2(mid-ll+1);
					int lm=st[ll][s]&st[mid-(1<<s)+1][s];
					if(lm>=k){
						l=mid;
					}
					else{
						r=mid-1;
					}
				}
				ans[++cnt]=l;
			}
		}
		for(int i=1;i<=cnt;i++){
			printf("%d ",ans[i]);
		}
		printf("\n");
	}
		
	return 0;
}

错误原因

对已学的知识没有利用好。

  • 33
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值