A.思维
是一个小思维题
我们知道2b−1 mod b=b−12b-1\ mod\ b=b-12b−1 mod b=b−1
这也是最大的
因此,对于[l,r][l,r][l,r] 我们找到 bbb使得2b−12b-12b−1为rrr 或者为r−1r-1r−1
但是当b<lb<lb<l时,我们便只能取lll
#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,72,3,5,72,3,5,7构成,且每一个数字最多只出现了一次
我们dfsdfsdfs找到所有的可能组成的数字,然后逐一检查即可
#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.思维+构造
分类讨论
对于所给二进制字符sss 长度为lenlenlen
-
如果存在索引iii s[i]==0s[i]==0s[i]==0且i>⌊len2⌋i>\lfloor \frac{len}{2}\rfloori>⌊2len⌋
那么我们便可以选取[1,i][1,i][1,i]与[1,i−1][1,i-1][1,i−1] 前者是后者的左移
-
否则一定对于i>⌊len2⌋i>\lfloor \frac{len}{2}\rfloori>⌊2len⌋s[i]==0s[i]==0s[i]==0
- s[⌊len2⌋]==0s[\lfloor \frac{len}{2}\rfloor]==0s[⌊2len⌋]==0: 选择[⌊len2⌋,len],[⌊len2⌋+1,len][\lfloor \frac{len}{2}\rfloor,len],[\lfloor \frac{len}{2}\rfloor+1,len][⌊2len⌋,len],[⌊2len⌋+1,len]
- s[⌊len2⌋]==1s[\lfloor \frac{len}{2}\rfloor]==1s[⌊2len⌋]==1:选择[⌊len2⌋,len−1],[⌊len2⌋+1,len][\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,−11,-11,−1的和
pre1pre1pre1:偶数位置乘−1-1−1
pre2pre2pre2:奇数位置乘−1-1−1
那么对于[l,r][l,r][l,r]
- lll为奇数,pre1[r]−pre1[l−1]pre1[r]-pre1[l-1]pre1[r]−pre1[l−1]
- 否则:pre2[r]−pre2[l−1]pre2[r]-pre2[l-1]pre2[r]−pre2[l−1]
得知,[l,r][l,r][l,r]之后呢 记其为sum(l,r)sum(l,r)sum(l,r)
- abs(sum)abs(sum)abs(sum)为奇数答案为111
- abs(sum)abs(sum)abs(sum)为偶数答案为222
先来看情况111 还是比较好理解的
我们找到l≤id≤rl\le id\le rl≤id≤r 使得sum(l,id)=sum(l,r)/2+1sum(l,id)=sum(l,r)/2+1sum(l,id)=sum(l,r)/2+1 sum(l,id−1)=sum(l,r)/2sum(l,id-1)=sum(l,r)/2sum(l,id−1)=sum(l,r)/2
那么sum(id+1,r)=sum(l,r)/2sum(id+1,r)=sum(l,r)/2sum(id+1,r)=sum(l,r)/2
那么当我们抽走ididid时,sum(id+1,r)=−sum(l,r)sum(id+1,r)=-sum(l,r)sum(id+1,r)=−sum(l,r)
那么sum(l,id−1)+sum(id+1,r)=0sum(l,id-1)+sum(id+1,r)=0sum(l,id−1)+sum(id+1,r)=0
至于偶数呢?
我们可以通过抽掉lll将其变为奇数的情况
通过操作,转化为其他的种类也是一种经典的套路
#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.二分
已经没有什么好说的了
关键就是如何找到应该要的索引了
记为函数solve(l,r)solve(l,r)solve(l,r)
找到[l,r][l,r][l,r]中,ididid sum(l,id)=sum(l,r)/2+1sum(l,id)=sum(l,r)/2+1sum(l,id)=sum(l,r)/2+1 sum(l,id−1)=sum(l,r)/2sum(l,id-1)=sum(l,r)/2sum(l,id−1)=sum(l,r)/2
我们已知了sum(l,id)sum(l,id)sum(l,id) 那么我们也就是知道了pre(id)pre(id)pre(id)
因此,我们将每一个pre(i)pre(i)pre(i)的值开一个vectorvectorvector
然后进vector[pre(id)]vector[pre(id)]vector[pre(id)]中找第一个>=l>=l>=l的作为ididid
#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]的下一个
- 如果存在sl′+i>sl+is_{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]为以后缀iii为结尾的最长上升子序列的长度
那么我们转移方式就是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]]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
这篇博客探讨了数学思维在编程问题解决中的重要性,包括分类讨论、暴力求解、构造法等策略。文章通过实例展示了如何利用这些方法解决二进制字符串处理、前缀和计算以及最长上升子序列等问题,强调了数学在信息技术领域的核心地位。
1296

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



