A.思维
是一个小思维题
我们知道 2 b − 1 m o d b = b − 1 2b-1\ mod\ b=b-1 2b−1 mod b=b−1
这也是最大的
因此,对于 [ l , r ] [l,r] [l,r] 我们找到 b b b使得 2 b − 1 2b-1 2b−1为 r r r 或者为 r − 1 r-1 r−1
但是当 b < l b<l b<l时,我们便只能取 l l l
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;
scanf("%d",&t);
while(t--){
int l,r;
scanf("%d%d",&l,&r);
int wtl=r%2?(r+1)/2:r/2+1;
if(l<=wtl){
printf("%d\n",r%wtl);
}else{
printf("%d\n",r%l);
}
}
return 0;
}
B.分类讨论+暴力
首先我们可以确定的一点是:如果所给的字符串中含有非素的个位数,那么我们直接输出这个个位数即可
其次,如果,含有某一相同的数字出现两次,那么我们就输出本数字两个即可
好的,去除了上面的情况我们可以发现,剩余的情况就是
所给字符串是由 2 , 3 , 5 , 7 2,3,5,7 2,3,5,7构成,且每一个数字最多只出现了一次
我们 d f s dfs dfs找到所有的可能组成的数字,然后逐一检查即可
#include<bits/stdc++.h>
using namespace std;
int a[55];
int n;
vector<int> vec;
void dfs(int pos,int num)
{
if (pos>n)
{
if (num!=0)vec.push_back(num);
return ;
}
dfs(pos+1,num*10+a[pos]);
dfs(pos+1,num);
}
int getlen(int num)
{
int ans=0;
while (num)
{
++ans;
num/=10;
}return ans;
}
bool check(int num)
{
if (num==1)return false;
for (int i=2;i<num;++i)if (num%i==0)return false;
return true;
}
int cnt[10];
int main()
{
ios::sync_with_stdio(0);
int t;cin>>t;
while(t--)
{
memset(cnt,0,sizeof(cnt));
cin>>n;
for (int i=1;i<=n;++i)
{
char ch;cin>>ch;
a[i]=(int)(ch-'0');
}
bool f = false;
for (int i=n;i>=1;--i)
{
cnt[a[i]]++;
if (a[i]!=2&&a[i]!=5&&a[i]!=3&&a[i]!=7)
{
cout<<"1\n"<<a[i]<<"\n";
f=true;
break;
}
}if (f)continue;
for (int i:{2,3,5,7})
{
if (cnt[i]>=2){
cout<<"2\n"<<i<<i<<"\n";
f=true;
break;
}
}if (f)continue;
vec.clear();
dfs(1,0);
sort(vec.begin(),vec.end());
for (int num:vec)
{
if (!check(num))
{
cout<<getlen(num)<<"\n"<<num<<"\n";
break;
}
}
}
}
C.思维+构造
分类讨论
对于所给二进制字符 s s s 长度为 l e n len len
-
如果存在索引 i i i s [ i ] = = 0 s[i]==0 s[i]==0且 i > ⌊ l e n 2 ⌋ i>\lfloor \frac{len}{2}\rfloor i>⌊2len⌋
那么我们便可以选取 [ 1 , i ] [1,i] [1,i]与 [ 1 , i − 1 ] [1,i-1] [1,i−1] 前者是后者的左移
-
否则一定对于 i > ⌊ l e n 2 ⌋ i>\lfloor \frac{len}{2}\rfloor i>⌊2len⌋ s [ i ] = = 0 s[i]==0 s[i]==0
- s [ ⌊ l e n 2 ⌋ ] = = 0 s[\lfloor \frac{len}{2}\rfloor]==0 s[⌊2len⌋]==0: 选择 [ ⌊ l e n 2 ⌋ , l e n ] , [ ⌊ l e n 2 ⌋ + 1 , l e n ] [\lfloor \frac{len}{2}\rfloor,len],[\lfloor \frac{len}{2}\rfloor+1,len] [⌊2len⌋,len],[⌊2len⌋+1,len]
- s [ ⌊ l e n 2 ⌋ ] = = 1 s[\lfloor \frac{len}{2}\rfloor]==1 s[⌊2len⌋]==1:选择 [ ⌊ l e n 2 ⌋ , l e n − 1 ] , [ ⌊ l e n 2 ⌋ + 1 , l e n ] [\lfloor \frac{len}{2}\rfloor,len-1],[\lfloor \frac{len}{2}\rfloor+1,len] [⌊2len⌋,len−1],[⌊2len⌋+1,len]
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e4+100;
int a[maxn];
int n;
int main()
{
ios::sync_with_stdio(0);
int t;cin>>t;
while (t--)
{
cin>>n;
for (int i=1;i<=n;++i)
{
char ch;cin>>ch;
a[i]=(int)(ch-'0');
}
bool f = false;
for (int i=n/2+1;i<=n;++i)
{
if (a[i]==0)
{
cout<<1<<" "<<i<<" "<<1<<" "<<i-1<<"\n";
f=true;
break;
}
}if (f)continue;
if (a[n/2]==1)cout<<n/2<<" "<<n-1<<" "<<n/2+1<<" "<<n<<"\n";
else cout<<n/2<<" "<<n<<" "<<n/2+1<<" "<<n<<"\n";
}
}
D1.思维
不是很难
我们可以通过维护两个前缀和来求出所有 [ l , r ] [l,r] [l,r]区间的 1 , − 1 1,-1 1,−1的和
p r e 1 pre1 pre1:偶数位置乘 − 1 -1 −1
p r e 2 pre2 pre2:奇数位置乘 − 1 -1 −1
那么对于 [ l , r ] [l,r] [l,r]
- l l l为奇数, p r e 1 [ r ] − p r e 1 [ l − 1 ] pre1[r]-pre1[l-1] pre1[r]−pre1[l−1]
- 否则: p r e 2 [ r ] − p r e 2 [ l − 1 ] pre2[r]-pre2[l-1] pre2[r]−pre2[l−1]
得知, [ l , r ] [l,r] [l,r]之后呢 记其为 s u m ( l , r ) sum(l,r) sum(l,r)
- a b s ( s u m ) abs(sum) abs(sum)为奇数答案为 1 1 1
- a b s ( s u m ) abs(sum) abs(sum)为偶数答案为 2 2 2
先来看情况 1 1 1 还是比较好理解的
我们找到 l ≤ i d ≤ r l\le id\le r l≤id≤r 使得 s u m ( l , i d ) = s u m ( l , r ) / 2 + 1 sum(l,id)=sum(l,r)/2+1 sum(l,id)=sum(l,r)/2+1 s u m ( l , i d − 1 ) = s u m ( l , r ) / 2 sum(l,id-1)=sum(l,r)/2 sum(l,id−1)=sum(l,r)/2
那么 s u m ( i d + 1 , r ) = s u m ( l , r ) / 2 sum(id+1,r)=sum(l,r)/2 sum(id+1,r)=sum(l,r)/2
那么当我们抽走 i d id id时, s u m ( i d + 1 , r ) = − s u m ( l , r ) sum(id+1,r)=-sum(l,r) sum(id+1,r)=−sum(l,r)
那么 s u m ( l , i d − 1 ) + s u m ( i d + 1 , r ) = 0 sum(l,id-1)+sum(id+1,r)=0 sum(l,id−1)+sum(id+1,r)=0
至于偶数呢?
我们可以通过抽掉 l l l将其变为奇数的情况
通过操作,转化为其他的种类也是一种经典的套路
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5+100;
int pre1[maxn],pre2[maxn];
int n,m;
int main()
{
ios::sync_with_stdio(0);
int t;cin>>t;
while (t--)
{
int tmp=1;
cin>>n>>m;
for (int i=1;i<=n;++i)
{
char ch;cin>>ch;
pre1[i] = tmp*(ch=='+'?1:-1);
pre2[i]=-pre1[i];
pre1[i]+=pre1[i-1];
pre2[i]+=pre2[i-1];
tmp=-tmp;
}cout<<endl;
while (m--)
{
int l,r;cin>>l>>r;
int res=0;
if (l&1)res=abs(pre1[r]-pre1[l-1]);
else res=abs(pre2[r]-pre2[l-1]);
if (res&1)cout<<1<<endl;
else if (res==0)cout<<0<<endl;
else cout<<2<<endl;
}
}
}
D2.二分
已经没有什么好说的了
关键就是如何找到应该要的索引了
记为函数 s o l v e ( l , r ) solve(l,r) solve(l,r)
找到 [ l , r ] [l,r] [l,r]中, i d id id s u m ( l , i d ) = s u m ( l , r ) / 2 + 1 sum(l,id)=sum(l,r)/2+1 sum(l,id)=sum(l,r)/2+1 s u m ( l , i d − 1 ) = s u m ( l , r ) / 2 sum(l,id-1)=sum(l,r)/2 sum(l,id−1)=sum(l,r)/2
我们已知了 s u m ( l , i d ) sum(l,id) sum(l,id) 那么我们也就是知道了 p r e ( i d ) pre(id) pre(id)
因此,我们将每一个 p r e ( i ) pre(i) pre(i)的值开一个 v e c t o r vector vector
然后进 v e c t o r [ p r e ( i d ) ] vector[pre(id)] vector[pre(id)]中找第一个 > = l >=l >=l的作为 i d id id
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5+100;
vector<int>G1[maxn<<1],G2[maxn<<1];
int pre1[maxn],pre2[maxn];
int a[maxn];
char s[maxn];
int n,q;
int solve(int l,int r)
{
if (l&1)
{
int val = pre1[r]-pre1[l-1];
int tar=abs(val)/2+1;
if (val<0)tar *= -1;
tar+=n+pre1[l-1];
return *lower_bound(G1[tar].begin(),G1[tar].end(),l);
}
else
{
int val = pre2[r]-pre2[l-1];
int tar=abs(val)/2+1;
if (val<0)tar *= -1;
tar+=n+pre2[l-1];
return *lower_bound(G2[tar].begin(),G2[tar].end(),l);
}
}
int main()
{
int t;scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&q);
for (int i=0;i<=n*2;++i)G1[i].clear(),G2[i].clear();
scanf("%s",s);
for (int i=1;i<=n;++i)
a[i] = (s[i-1]=='+'?1:-1);
for (int i=1;i<=n;++i)
{
if (i&1)
{
pre1[i]=pre1[i-1]+a[i];
pre2[i]=pre2[i-1]-a[i];
G1[pre1[i]+n].push_back(i);
G2[pre2[i]+n].push_back(i);
}
else
{
pre1[i]=pre1[i-1]-a[i];
pre2[i]=pre2[i-1]+a[i];
G1[pre1[i]+n].push_back(i);
G2[pre2[i]+n].push_back(i);
}
}
while (q--)
{
int l,r;scanf("%d %d",&l,&r);
if (l&1)
{
if (pre1[r]-pre1[l-1]==0)
{
printf("0\n");
}
else if ((pre1[r]-pre1[l-1])&1)
{
printf("1\n%d\n",solve(l,r));
}
else
{
printf("2\n%d ",l);
printf("%d\n",solve(l+1,r));
}
}
else
{
if (pre2[r]-pre2[l-1]==0)
{
printf("0\n");
}
else if ((pre2[r]-pre2[l-1])&1)
{
printf("1\n%d\n",solve(l,r));
}
else
{
printf("2\n%d ",l);
printf("%d\n",solve(l+1,r));
}
}
}
}
}
E.思维+后缀数组
首先要意识到一点重要的结论:
如果[l,r]在最长上升子序列中,那么[l,r+1],[l,r+2], … \ldots …[l,n]也在序列中!!
证明,假设 [ l ′ , r ′ ] [l',r'] [l′,r′]为最长上升子序列中 [ l , r ] [l,r] [l,r]的下一个
- 如果存在 s l ′ + i > s l + i s_{l'+i}>s_{l+i} sl′+i>sl+i那么 [ l , r + 1 ] < [ l ′ . r ′ ] [l,r+1]<[l'.r'] [l,r+1]<[l′.r′]
- 否则, [ l , r ] [l,r] [l,r]就是 [ l ′ , r ′ ] [l',r'] [l′,r′]的前缀!
因此,记 f [ i ] f[i] f[i]为以后缀 i i i为结尾的最长上升子序列的长度
那么我们转移方式就是 f [ i ] = m a x ( f [ j ] + n − l c p [ i ] [ j ] − i + 1 ) ∣ s [ i + l c p [ i ] [ j ] ] < s [ j + l c p [ i ] [ j ] ] f[i]=max(f[j]+n-lcp[i][j]-i+1)|s[i+lcp[i][j]]<s[j+lcp[i][j]] f[i]=max(f[j]+n−lcp[i][j]−i+1)∣s[i+lcp[i][j]]<s[j+lcp[i][j]]
#include <bits/stdc++.h>
using namespace std;
const int N=10010;
int Q,n,ans,f[N],lcp[N][N];
char s[N];
int main()
{
scanf("%d",&Q);
while (Q--)
{
scanf("%d%s",&n,s+1);
for (int i=1;i<=n;i++)
lcp[i][n+1]=lcp[n+1][i]=0,f[i]=n-i+1;
for (int i=n;i>=1;i--)
for (int j=n;j>=1;j--)
lcp[i][j]=(s[i]==s[j]) ? lcp[i+1][j+1]+1 : 0;
for (int i=1;i<=n;i++)
for (int j=1;j<i;j++)
if (i+lcp[i][j]<=n && s[j+lcp[i][j]]<s[i+lcp[i][j]])
f[i]=max(f[i],f[j]+n-(i+lcp[i][j]-1));
ans=0;
for (int i=1;i<=n;i++)
ans=max(ans,f[i]);
cout<<ans<<"\n";
}
return 0;
}
//代码出自stoorz