牛客周赛 Round 69
题解
赛时做题
A
入门题
等差数列,找公差,构造第三个即可
B
题意简单,考察字符串转化成数字
C
几何题,大概初中难度,用全等或者向量都可以(初做时废了半天劲,果然上了大学就废了
赛后补题
D
纯暴力,但是可以收获的有两点
- 将二维转化成一维处理
- bitset的使用和二进制操作
__builtin_popcount(i)返回i的二进制数中有多少个1
#include<bits/stdc++.h>
using namespace std;
int n,m,q;
bitset<49>b[9];//设置多少位的二进制数
bitset<49>a;
int ans;
int main(){
cin>>n>>m>>q;ans=q+1;
for(int k=0;k<=q;++k){
string s,x;
for(int i=1;i<=n;++i){//将矩阵转化成一维
cin>>x;
s+=x;
}
b[k]=bitset<49>(s);
}
int check=0;
for(int i=0;i<(1<<q);++i){
if(__builtin_popcount(i)>=ans) continue;
bitset<49>c;
for(int j=0;j<q;++j)
if((i>>j)&1){
c|=b[j+1];
}
if((c|b[0])==(1ll<<(m*n))-1 && (c&b[0])==0){
ans=__builtin_popcount(i);
check=i;
}
}
if(ans==q+1) ans=-1;
cout<<ans<<endl;
if(ans!=-1){
for(int i=0;i<q;++i){
if(check>>i&1){
cout<<i+1<<" ";
}
}
}
return 0;
}
E
值得学习的点:
对前缀和的精妙使用,还有前驱后驱的利用
从暴力的思想逐渐优化:
暴力找第一个点 ,找到后找第二个切割点(统计共有多少个点可以)
优化:
-
判断区间内正数个数,也用前缀和
-
(第二步用后缀和就可以优化)
-
判断切割点用前缀和判断区间和是否满足要求
#include<bits/stdc++.h>
using namespace std;
int n;
const int maxn=2e5+10;
int f[maxn];//后缀和 统计i点之后有多个合法的(可以切成全部和的1/3且有正数的)切割点
int nt[maxn];//后驱 i点及以后的第一个正数的位置(下标)
int ne[maxn];//前缀和统计正数的个数
long long sum[maxn];//前缀和
int a[maxn];
int main(){
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
sum[i]=sum[i-1]+a[i];
ne[i]=ne[i-1]+(a[i]>0);//正数个数的前缀和
}
if(sum[n]%3){//不能均分
puts("0");
return 0;
}
nt[n+1]=n+1;
for(int i=n;i>=1;--i){
f[i]=f[i+1]+(sum[n]-sum[i-1]==sum[n]/3 && ne[n]-ne[i-1]);//i点之后 合法的第二个切割点总数
nt[i]=a[i]>0?i:nt[i+1];//下一个正数的位置
}
long long ans=0;
for(int i=1;i<=n;++i){
if(sum[i]==sum[n]/3 && ne[i]){//找第一个切割点
int t=nt[i+1];
if(t<n)ans+=f[t+1];
}
}
cout<<ans<<endl;
return 0;
}