Codeforces Round 895 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 (n−k+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=i−1), 对于每个正整数 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+li−x)和 b = m a x ( x , r i + l i − x ) b=max(x,r_i+l_i-x) b=max(x,ri+li−x)。
将 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;
}
错误原因
对已学的知识没有利用好。