CF-938
C
没啥好分析的,就记录一下我因为没有清空s[n+1]、上取整写成了下取整卡了一个多小时(╬▔皿▔)╯
c++
const int N=2e5+5;
int a[N],p[N],s[N];
void solve(){
int n,k;cin>>n>>k;
int sum=0;
rep(i,1,n){
cin>>a[i];
p[i]=p[i-1]+a[i];
}
if(p[n]<=k){
cout<<n<<endl;
return;
}
s[n+1]=0;//
per(i,n,1){
s[i]=s[i+1]+a[i];
}
int ans=0;
int l=0,r=n,mid;
while(r-l>1){
mid=l+r>>1;
if(p[mid]<(k+1)/2) l=mid;
else r=mid;
}
//r为第一个p[i]>=(k+1)/2的下标
if(p[r]>(k+1)/2) ans+=r-1;
else if(p[r]==(k+1)/2) ans+=r;
l=1,r=n+1;
while(r-l>1){
mid=l+r>>1;
if(s[mid]<(k+1)/2) r=mid;
else l=mid;
}
//l为第一个p[i]>=(k+1)/2的下标
if(s[l]>k/2) ans+=n-l;
else if(s[l]==k/2) ans+=n-l+1;
cout<<ans<<endl;
}
D
可惜我赛前不久还做过滑动窗口的题,赛时却一直调不出来 /_ \
分析
就是在数组a中找有多少个长度为m的子段,满足子段内元素与b数组的元素至少有k个相同,我们可以枚举长度为m的子段,用桶数组维护当前子段的元素种类数,更新就是当长度大于m时,减去子段第一个元素的种类数,再判断一下此时该元素的种类数是否小于b数组中的种类数,是的话匹配数now--
代码
c++
const int N=2e5+5,M=1e6+5;
int a[N],b[N],tb[M],ta[M];
void solve(){
int n,m,k;cin>>n>>m>>k;
rep(i,1,n){
cin>>a[i];
}
rep(i,1,m){
cin>>b[i];
tb[b[i]]++;
}
int j=0,now=0,ans=0;
rep(i,1,n){
ta[a[i]]++;//对于每个新元素,将其加入桶中
if(ta[a[i]]<=tb[a[i]]) now++;
j=i-m;
if(j>=1){//子段长度大于m时,删除第一个元素的种类数
ta[a[j]]--;
if(ta[a[j]]<tb[a[j]]) now--;//若相等则说明删除该元素对匹配数无影响
}
if(j>=0&&now>=k) ans++;
}
//清空桶数组,用memset会t
rep(i,1,n) ta[a[i]]=0;
rep(i,1,m) tb[b[i]]=0;
cout<<ans<<endl;
}
E
这题让我想起了之前还没补的一道题 3492: 王阿姨处理信号,也是01字符串,也需要做区间处理......
知识点
利用异或差分数组进行区间取反
常用在只有01两种元素的序列中
c++
int n;string s;cin>>n>>s;
s=' '+s;
int l,r;cin>>l>>r;
//将区间[l,r]取反
d[l]^=1,d[r+1]^=1;
rep(i,1,n){
d[i]^=d[i-1];
cout<<((s[i]-'0')^d[i]);
}
分析
从大到小枚举取反区间的长度k,若遇到s[i]=0,则对[i,i+k-1]进行区间取反,若有k合法就跳出
代码
c++
const int N=1e4+5;
int d[N];
void solve(){
int n;string s;cin>>n>>s;
s=' '+s;
per(k,n,1){//枚举取反区间
bool f=1;
rep(i,1,n) d[i]=0;//异或差分数组,d[l]=d[r]=1表示[l,r-1]均取反
rep(i,1,n){
d[i]^=d[i-1];//类似差分数组做前缀和还原为当前数
int x=(s[i]-'0');
//cout<<k<<" "<<i<<" "<<x<<endl;
if(x^d[i]==0){
/*
若x=d[i]=1,表示当前数此前已被取反为0,需要再取反
若x=d[i]=0,表示当前数原本就是0,需要取反
*/
if(i+k-1>n){//若取反区间的终点大于n,由于已无法操作,故当前k不可行
f=0;
break;
}
d[i]^=1;
d[i+k]^=1;
}
}
if(f){
cout<<k<<endl;
break;
}
}
}