T1 一道复杂的构造题
注意到
n
n
n 的数据范围是大于
1
1
1 的,那么说明这个序列中一定有一个区间包含
1
1
1 ,那么这个区间的
g
c
d
(
)
gcd()
gcd() 就是
1
1
1 ,显然,当
m
m
m 为偶数时,无法构造。
将
m
m
m 分成若干个数的的 按位或 运算值,最简单的方案就是把
m
m
m 当做二进制,对于每一位的
1
1
1 分一组,
m
m
m 最高位的
1
1
1 必须得小于等于
n
n
n,否则就无法构造。考虑如何快速求出一个数二进制最高位的值,不难想到最高位可以用
l
o
g
2
(
)
log_2()
log2() 来求出:
int highbit(int x){
return 1<<int(log2(x));
}
接下来我们遍历 j j j 从 h i g h b i t ( m ) highbit(m) highbit(m) 到 1 1 1 ,再遍历 i i i 从 1 1 1 到 n n n ,把所有能被 j j j整除的 i i i 放到一组,时间复杂度为 O ( log m × n ) O(\log{m}\times n) O(logm×n) ,轻松的过了这道题。
#include<bits/stdc++.h>
using namespace std;
int n,m;
queue<int>q;
int vis[100005];
int highbit(int x){
return 1<<int(log2(x));
}
int main(){
scanf("%d%d",&n,&m);
if(highbit(m)>n || m%2==0) printf("-1");
else{
int j=highbit(m),k=m,cnt=0;;
while(k>0){
int sum=0;
cnt++;
for(int i=1;i<=n;i++){
if(i%j==0 && vis[i]==0){
sum++;
vis[i]=1;
printf("%d ",i);
}
}
q.push(sum);
k-=j;
j=highbit(k);
}
printf("\n%d\n",cnt);
int ls=0;
while(!q.empty()){
printf("%d %d\n",ls+1,ls+q.front());
ls+=q.front();
q.pop();
}
}
return 0;
}
T2 茳侨串
题目要求将给定字符串变为恰好包含
3
3
3 个形如
01
01
01 和
10
10
10 的子串,那么这个字符串一定是
00
…
011
…
100
…
0
00\dots 011\dots 100\dots 0
00…011…100…0 或
11
…
100
…
011
…
1
11\dots 100\dots 011\dots 1
11…100…011…1,两种情况本质相同,下面就以第一种情况为例。
先考虑暴力做法,枚举
3
3
3 个端点,遍历每个区间,求出需要改的总数,时间复杂度
O
(
n
3
)
O(n^3)
O(n3),可以拿到
50
50
50 分。
for(int i=2;i<=n-1;i++){
sum1=0x7f7f7f7f,sum2=0x7f7f7f7f;
for(int j=1;j<i;j++){
d=0;
for(int k=1;k<=j;k++){
if(a[k]!=0) d++;
}
for(int k=j+1;k<=i;k++){
if(a[k]!=1) d++;
}
sum1=min(sum1,d);
}
for(int j=i+1;j<n;j++){
d=0;
for(int k=i+1;k<=j;k++){
if(a[k]!=0) d++;
}
for(int k=j+1;k<=n;k++){
if(a[k]!=1) d++;
}
sum2=min(sum2,d);
}
ans=min(ans,sum1+sum2);
}
对于区间 [ l , r ] [l,r] [l,r] 内要求有几个需要改的,若需都改成 1 1 1 ,那这段区间的区间和就得是 ( r − l + 1 ) (r-l+1) (r−l+1) ,要改的数量就是这个结果和实际这段区间和的差;同理,若需都改成 0 0 0 ,那么要改的数量就是这段的区间和。如何维护区间和呢,用前缀和就可以了。此时的时间复杂度为 O ( N 2 ) O(N^2) O(N2),可以拿到 70 70 70 分。
for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
for(int i=2;i<=n-1;i++){
sum1=0x7f7f7f7f,sum2=0x7f7f7f7f;
for(int j=1;j<i;j++){
sum1=min(sum1,2*s[j]+i-j-s[i]);
}
for(int j=i+1;j<n;j++){
sum2=min(sum2,2*s[j]+n-j-s[n]-s[i]);
}
ans=min(ans,sum1+sum2);
}
我们发现对于这一种情况, i , s [ i ] , s [ n ] , n i,s[i],s[n],n i,s[i],s[n],n 都为定值,剩下的 2 × s [ j ] − j 2 \times s[j]-j 2×s[j]−j可以用前缀最小值和后缀最小值直接求出,同理,第二种情况可以直接由前缀最大值和后缀最大值求出。经过这一次优化后,时间复杂度变为 O ( n ) O(n) O(n) , 100 p t s 100pts 100pts 通过。
for(int i=0;i<=n+1;i++){
smin[i]=tmin[i]=0x7f7f7f7f;
smax[i]=tmax[i]=0x80808080;
}
for(int i=1;i<=n;i++){
smin[i]=min(smin[i-1],t[i]);
smax[i]=max(smax[i-1],t[i]);
}
for(int i=n-1;i>=1;i--){
tmin[i]=min(tmin[i+1],t[i]);
tmax[i]=max(tmax[i+1],t[i]);
}
int ans=0x7f7f7f7f;
for(int i=2;i<n-1;i++){
ans=min(ans,smin[i-1]+i-s[i]+tmin[i+1]+n-s[n]-s[i]);
ans=min(ans,-smax[i-1]+s[i]-tmax[i+1]+s[i]-i+s[n]);
}
657

被折叠的 条评论
为什么被折叠?



