目录
- 前言
- 题目一
- 题目二
- 题目三
- 题目四
- 题目五
- 题目六
- 题目七
- 题目八
- 题目九
- 题目十
- 题目十一
- 题目十二
- 题目十三
- 题目十四
- 题目十五
- 题目十六
- 题目十七
- 题目十八
- 题目十九
- 题目二十
- 题目二十一
- 题目二十二
- 题目二十三
- 题目二十四
- 题目二十五
前言
题目一
原题链接:lanqiao1389
题目
代码
#include <iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
int data[200];
for(int i = 0 ; i < 200 ; i ++)data[i] = 4 * i + 6;
int n;
cin>>n;
cout<<lower_bound(data,data+200,n)-data<<endl;
return 0;
}
题解分析
本题可以使用整数二分查找解题,这里采用C++的lower_bound
库函数,使解题更迅速、简洁。
lower_bound(st,ed,x)
库函数需要输入三个参数,前两个参数是迭代器,最后一个参数为目标元素。其返回第一个大于等于目标元素的迭代器。如果没有,就返回最后一个元素的下一个地址。- 注意:查找区间
[st ,ed)
为左闭右开区间,所以题解中是[data,data+200)
。 - 该库函数默认在升序序列中使用,如果要在降序序列中使用,则要加上比较函数。
lower_bound(st,ed,x,greater<int>())
题目二
原题链接:lanqiao1265
题目
代码
#include <iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main()
{
vector<int>s;
int N;
cin>>N;
s.resize(N);
for(int i=0;i<N;i++){
cin>>s[i];
}
sort(s.begin(),s.end());
for(int ele:s){
cout<<ele<<" ";
}
cout<<endl;
sort(s.begin(),s.end(),greater<int>());
for(int ele:s){
cout<<ele<<" ";
}
cout<<endl;
return 0;
}
题解分析
本题可以使用数组解题,这里使用了STL中的vector
容器解题,遍历起来更加方便。
-
题目思路是使用
sort
库函数进行升序排序,再降序排序,各自遍历输出一遍即可。 -
sort
库函数的参数与lower_bound
一样为左闭右开区间,加上greater<int>()
进行降序排序。 -
当然,这里的降序排序可以使用
reverse
进行反转,代码为reverse(s.begin(),s.end());
。此时,数据会发生反转,再输出即可。 -
注意:
vector
容器在输入时要先用resize
分配空间。也可以先录入给临时变量,再用push_back()
输入到vector
容器中。for(int i=0;i<N;i++){
int t;cin>>t;
s.push_back(t);
}
题目三
原题链接:lanqiao497
题目
代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
vector<int>s;
int n;
cin>>n;
s.resize(n);
for(int i=0;i<n;i++){
cin>>s[i];
}
cout<<*max_element(s.begin(),s.end())<<endl;
cout<<*min_element(s.begin(),s.end())<<endl;
long long sum=0;
for(int ele:s){
sum+=ele;
}
cout<<fixed<<setprecision(2)<<1.0*sum/n<<endl;
return 0;
}
题解分析
本题解使用了万能头文件bits/stdc++.h
、max_element/min_element
库函数和vector
容器。
-
本题思路很简单,使用
max_element/min_element
找到最大值和最小值。然后,由于数值较大,使用long long
类型的变量来记录总值,最后输出平均值即可。 -
注意:
max_element/min_element
库函数返回的是区间中目标值的迭代器,所以要用*
获取迭代器对应的值。 -
cout
输出使用fixed<<setprecision(x)
保留x位小数。
题目四
原题链接:lanqiao1113
题目
代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
int m;
cin>>m;
string a;
queue<string> v,n;
for(int i=0;i<m;i++){
cin>>a;
if(a=="IN"){
string name,p;cin>>name>>p;
if(p=="V")v.push(name);
else n.push(name);
}
else{
string p;cin>>p;
if(p=="V")v.pop();
else n.pop();
}
}
while(v.size()){
cout<<v.front()<<endl;
v.pop();
}
while(n.size()){
cout<<n.front()<<endl;
n.pop();
}
return 0;
}
题解分析
本题情景是银行窗口排队,可以想到使用queue
容器(队列)解决。有两个窗口就使用两个queue
容器,然后根据题目分情况解题即可。
-
queue
容器使用string
类型,进行字符串输入。 -
q.push()
表示添加元素;q.pop()
表示移除队头元素;q.front()
表示获取队头元素;q.size()
表示队列里的元素个数; -
队列遍历输出:
while(q.size()){
cout<<q.front()<<endl;
q.pop();
}
题目五
原题链接:lanqiao2928
题目
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+9;
char s[N];
int main()
{
int n,x;cin>>n>>x;
cin>>s+1;
sort(s+1,s+n+1);
if(s[1]==s[n]){
for(int i=1;i<=n/x+(n%x?1:0);i++)cout<<s[1];
}
else if(s[1]==s[x]){
for(int i=x;i<=n;i++)cout<<s[i];
}
else{
cout<<s[x];
}
return 0;
}
题解分析
找规律的贪心,考验思维,将字符串分类讨论,设计贪心策略。
- 先给字符串排序,然后我们可以分为三类讨论:
- 1)字符串全相等 (假设全
a
),那就是尽量使得每个人分到的字符串的最大长度尽可能小就行。 - 2)
s[x]== s[1]
,说明第x
个是最小的字符带着后面所有的字符一起输出。 - 3)
s[x]!=s[1]
,后面一坨字符可以直接丢到s[1]
后面,分给第一个同学。
- 1)字符串全相等 (假设全
题目六
原题链接:lanqiao741
题目
代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
priority_queue<long long,vector<long long>,greater<long long>>pq;
int n;
cin>>n;
for(int i=0;i<n;i++){
long long t;cin>>t;
pq.push(t);
}
long long sum=0;
while(pq.size()>1){
long long f=pq.top();pq.pop();
long long e=pq.top();pq.pop();
sum+=f+e;
pq.push(f+e);
}
cout<<sum;
return 0;
}
题解分析
本题需要使用贪心策略和优先队列进行解题。
- 优先队列
priority_queue
使用堆实现,其默认为=大根堆== ,想要使用小根堆则要使用比较函数。 - 使用贪心策略是因为每次合成最小的两堆果子可以使得耗费的体力最小 ,由局部最优解得到最终最优解。
- 注意:合成后的果子堆必须重新放入优先队列中 ,优先队列会根据其值将其放在合适的地方以确保最后能得到最优解。
题目七
原题链接lanqiao549
题目
代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> a(n + 2, vector<int>(m + 2, 0)); // 初始化矩阵 a
vector<vector<int>> b(n + 2, vector<int>(m + 2, 0)); // 初始化矩阵 b
// 输入矩阵 a 的内部元素
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}
// 计算雷区
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i][j] == 1) {
b[i][j] = 9; // 标记雷
}
else {
int count = 0; // 统计周围雷的数量
for (int di = -1; di <= 1; di++) {
for (int dj = -1; dj <= 1; dj++) {
if (di == 0 = 0) continue; // 跳过自身
count += a[i + di][j + dj];
}
}
b[i][j] = count;
}
}
}
// 输出结果矩阵
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cout << b[i][j] << " ";
}
cout << endl; // 每行结束后换行
}
return 0;
}
题解分析
本题是一道模拟题,基于扫雷游戏而来。
- 类似于这种对二维数组中某一位置的周围进行操作的题目,必须考虑边界问题 ,可以在其周围再加一圈的空间以确保不会越界。
- 注意:类似于这种题目,在对原二维数组a进行修改操作前,必须先将其赋值给临时二维数组b,对临时二维数组b进行修改,最后记得赋值回原二维数组a 这样是为了避免数据干扰 如果直接在原数组 a 上进行标记和计算结果的存储,那么在计算后续格子周围雷数时,前面已经被修改的元素值会干扰计算,导致结果出错。而使用临时二维数组 b ,可以在不改变原数组 a 的情况下,独立地计算和存储每个格子周围雷的数量以及雷的标记。
题目八
原题链接:lanqiao551
题目
代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n,m;
cin>>n>>m;
int a[n+2][m+2]={0};
int t;cin>>t;
for(int i=1;i<=t;i++){
int N,M;cin>>N>>M;
a[N][M]=1;
}
int k;cin>>k;
while(k--){
int t[n+2][m+2];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
t[i][j]=a[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]==1){
if(a[i-1][j]==0)t[i-1][j]=1;
if(a[i][j-1]==0)t[i][j-1]=1;
if(a[i][j+1]==0)t[i][j+1]=1;
if(a[i+1][j]==0)t[i+1][j]=1;
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
a[i][j]=t[i][j];
}
}
}
int sum=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]==1)sum++;
}
}
cout<<sum;
return 0;
}
题解分析
本题同样为模拟题,与扫雷题不同之处在于只用对某一位置的上下左右方格进行操作 。
- 与扫雷相同,需考虑边界和临时数组。
题目九
原题链接lanqiao2489
题目
代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=50;
int a[N];
int main()
{
string s="2021ABCD";
for(int i=0;i<s.size();i++){
if(s[i]>='0''9')a[i+1]=s[i]-'0';
else a[i+1]=s[i]-'A'+10;
}
ll x=0;
for(int i=1;i<=s.size();i++){
x=x*16+a[i];
}
cout<<x;
return 0;
}
题解分析
本题思路简单,十六进制转十进制,套用模板即可。
- 需要注意的是,任意进制转十进制,需要将每一位转换为
int
类型放入数组中,然后对数组进行操作。
题目十
原题链接:lanqiao1230
题目
代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=50;
int a[N];
char ch[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
int main()
{
int t;cin>>t;
while(t--){
int n,m;cin>>n>>m;
string s;cin>>s;
int len=s.size();
for(int i=0;i<len;i++){
if(s[i]>='0''9')a[i+1]=s[i]-'0';
else a[i+1]=s[i]-'A'+10;
}
ll x=0;
for(int i=1;i<=len;i++){
x=x*n+a[i];
}
string ans;
while(x){
ans+=ch[x%m];
x/=m;
}
reverse(ans.begin(),ans.end());
cout<<ans<<endl;
}
return 0;
}
题解分析
本题是N进制转为M进制,可以以十进制为桥梁 。先将N进制转换为十进制 ,再将十进制转换为M进制。
- 十进制数转换任意进制数 ,使用
string
类型的变量承接答案。 - 题中的
M
不会超过十六进制 ,所以可以定义一个字符数组 ,再取模使用即可。 - 注意:十进制转任意进制是倒序输入字符串的,所以最后必须用
reverse
反转字符串得正解。
题目十一
原题链接:lanqiao2296
题目
代码
#include <bits/stdc++.h>
using namespace std;
const int N=50;
int a[N];
char ch[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
int main()
{
int sum=0;
for(int i=2023;i<=10000;i++){
int t=i;
string ans;
while(t){
ans+=ch[t%2];
t/=2;
}
reverse(ans.begin(),ans.end());
int len=ans.size();
int w=0;
for(int j=len-1;j>len-7;j--){
if(ans[j]=='0')w++;
}
if(w==6){
cout<<i;
break;
}
}
return 0;
}
题解分析
本题十进制转换为二进制。
- 但要注意的是,二进制位是左高右低,所以从最后进行判断六个
0
。
题目十二
原题链接:lanqiao2406
题目
代码
#include <bits/stdc++.h>
using namespace std;
char ch[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
int main() {
for (int i = 2023; i <= 1000000000; i++) {
int t = i;
string ans;
while (t) {
ans += ch[t % 16];
t /= 16;
}
reverse(ans.begin(), ans.end());
int len = ans.size();
int w = 0;
for (int j = 0; j <len; j++) {
if (ans[j] >= 'A' && ans[j] <= 'F') w++;
}
if (w == len ) {
cout << i << endl; // 直接输出 i
break;
}
}
return 0;
}
题解分析
本题相较于上一题只是条件改变了而已,套用模板即可。
题目十三
原题链接:lanqiao2406
题目
代码
#include <bits/stdc++.h>
using namespace std;
char ch[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
int fun (int x,int k){
string ans;
while(x){
ans+=ch[x%k];
x/=k;
}
int w=0;
for(char ele:ans){
w+=ele-'0';
}
return w;
}
int main()
{
int sum=0;
for(int i=1;i<=2024;i++){
if(fun(i,2)==fun(i,4)){
sum++;
}
}
cout<<sum;
return 0;
}
题解分析
本题是十进制转换为二进制和四进制。由于转换过程有重复部分,所以使用函数进行包装。
题目十四
原题链接:lanqiao2080
题目
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int main(){
long long n;cin>>n;
long long a[N];
for(int i=1;i<=n;i++){
cin>>a[i];
}
long long prefix[N];
prefix[0]=0;
for(int i=1;i<=n;i++)prefix[i]=prefix[i-1]+a[i];
long long sum=0
for(int i=1;i<=n;i++)sum+=a[i]*(prefix[n]-prefix[i]);
cout<<sum;
return 0;
}
题解分析
本题需要使用前缀和。
- 构建前缀和:
for(int i=1;i<=n;i++)prefix[i]=prefix[i-1]+a[i];
- 提出公因子求区间和:
for(int i=1;i<=n;i++)sum+=a[i]*(prefix[n]-prefix[i]);
题目十五
原题链接:lanqiao3382
题目
代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long ;
const int N=1e5+9;
const int p=1e9+7;
ll a[6][N],prefix[6][N];
int main(){
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[1][i];
for(int i=2;i<=5;i++){
for(int j=1;j<=n;j++){
a[i][j]=a[i-1][j]*a[1][j]%p;
}
}
for(int i=1;i<=5;i++){
for(int j=1;j<=n;j++){
prefix[i][j]=(prefix[i][j-1]+a[i][j])%p;
}
}
while(m--){
int l,r,k;cin>>l>>r>>k;
cout<<(prefix[k][r]-prefix[k][l-1]+p)%p<<endl;
}
return 0;
}
题解分析
本题仍然使用前缀和,由于k
比较小,所以我们可以处理出五个数组分别表示不同的次方。
- 例如:
a3[]
中的元素都是数组a中元素的3
次方。 - 再对五个数组预处理出前缀和,对于每次询问利用前缀和的性质可
O(1)
解决。 - 注意:
prefix[k][r]-prefix[k][l-1]
可能为负数,因此需要加上模数p
再取模,确保结果为正数。
题目十六
原题链接:lanqiao3291
题目
代码
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],diff[N];
int main()
{
int n,m;
while(cin>>n>>m){
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++)diff[i]=a[i]-a[i-1];
while(m--){
int x,y,z;cin>>x>>y>>z;
diff[x]+=z;
diff[y+1]-=z;
}
for(int i=1;i<=n;i++)a[i]=a[i-1]+diff[i];
for(int i=1;i<=n;i++)cout<<a[i]<<" \n"[i==n];
}
return 0;
}
题解分析
本题利用差分数组对数组a
进行区间修改即可。
- 差分数组的构建:
for(int i=1;i<=n;i++)diff[i]=a[i]-a[i-1];
- 区间加
z
:diff[x]+=z; diff[y+1]-=z;
题目十七
原题链接:lanqiao1276
题目
5 3
2 2 2 1 5
1 3 3
4 5 5
1 1 -100
0 5 5 6 10
运行限制
- 最大运行时间:1s
- 最大运行内存: 128M
代码
#include <bits/stdc++.h>
using namespace std;
const int N =5000010;
using ll=long long;
ll a[N],diff[N];
int main()
{
a[0]=diff[0]=0;
int n,q;cin>>n>>q;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)diff[i]=a[i]-a[i-1];
while(q--){
int l,r,x;
cin>>l>>r>>x;
diff[l]+=x;
diff[r+1]-=x;
}
for(int i=1;i<=n;i++)a[i]=a[i-1]+diff[i];
for(int i=1;i<=n;i++){
if(a[i]<0)cout<<0ll<<" ";
else
cout<<a[i]<<" ";
}
return 0;
}
题解分析
本题利用差分数组对数组a
进行区间修改即可。
- 注意:输出时亮度为负数则输出
0
,且本题需要用到long long
类型。
题目十八
原题链接:lanqiao3419
题目
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
char s[N];
int prefix[N];
int main(){
cin>>s+1;
int n=strlen(s+1);
for(int i=1;i<=n;i++){
prefix[i]=prefix[i-1]+(s[i]=='L'?1:-1);
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
if(prefix[j]-prefix[i-1]==0)ans=max(ans,j-i+1);
}
}
cout<<ans<<endl;
return 0;
}
题解分析
- 将
L
看做1
,Q
看做-1
,只有当某个区间的和为0
时,字符串是平衡的。 - 我们可以预处理出前缀和,然后枚举所有区间(这一步的时间复杂度是
O(n^2)
的),得到所有平衡区间的长度最后取大输出即可。
题目十九
原题链接:lanqiao3412
题目
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
using ll=long long;
ll a[N];
int main()
{
ll n;cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+n+1);
ll m=1e9+10;
for(int i=2;i<=n;i++){
ll t=a[i]-a[i-1];
m=min(m,t);
}
cout<<m;
return 0;
}
题解分析
- 本题是简单排序模型。
- 要将战斗力分为两部分,为了使得差距最小,我们可以将战斗力排序后,找一个位置进行划分,使得左边的都在
a
,右边的都在b
,从而这个差距就是这个位置两边的战斗力差距,说明差距的取值仅有n-1
种,枚举即可。 - 当混乱的数据不好处理且排序不影响答案时,尝试先排序再分析。
题目二十
原题链接:lanqiao545
题目
代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
priority_queue<ll,vector<ll>,greater<ll>>pq;
int main()
{
ll n;cin>>n;
for(int i=1;i<=n;i++){
int e;cin>>e;
pq.push(e);
}
ll ans=0;
while(pq.size()>1){
ll t1=pq.top();pq.pop();
ll t2=pq.top();pq.pop();
int sum=t1+t2;
ans+=sum;
pq.push(sum);
}
cout<<ans;
return 0;
}
题解分析
本题是总操作数一定情况下的最小代价模型。
- 我们知道这里一共需要进行的操作次数一定是
n-1
次,那么贪心地想,如果每次选择代价最小的两个部落合并,不仅可以使得当前代价最小,还可以使得后续合并的代价也尽可能小。 - 部落大小通过优先队列来维护。
题目二十一
原题链接:lanqiao532
题目
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int main()
{
int w,n;cin>>w>>n;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+n+1);
int l=1,r=n;
int ans=0;
while(l<=r){
if((a[r]+a[l]>w)||(a[r]>w)){
ans++;
r--;
}
else{
ans++;l++,r--;
}
}
cout<<ans;
return 0;
}
题解分析
本题是最小数目的贪心模型
- 为了使组数最小,我们应该使得每一组内尽可能装两种礼物(最多只能装两件),尽量占满一组的容量。
- 先进行升序排序,然后使用对撞指针,对右指针进行判断,如果其与左指针相加或其本身大于
W
,就独自为一组,否则,两指针为一组。 - 这里的贪心策略为,每一个贵的礼物带一个便宜的成一组。
题目二十二
原题链接:lanqiao1371
题目
abcba
Y
示例 2
abcbb
N
运行限制
- 最大运行时间:1s
- 最大运行内存: 128M
代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
string s;
cin>>s;
int l=0,r=s.size()-1;
while(l<=r){
if(s[l]==s[r]){
l++,r--;
}
else{
cout<<'N';
return 0;
}
}
cout<<'Y';
return 0;
}
题解分析
本题使用对撞指针求解。
- 首先引入了
<bits/stdc++.h>
头文件,使用了std
命名空间。在main
函数中,定义了一个字符串s
。然后通过cin
从标准输入读取一个字符串存入s
。接着定义了两个变量l
和r
,分别初始化为字符串的首字符位置和尾字符位置。 - 之后进入一个循环,当
l
小于等于r
时循环继续。在循环中,如果s[l]
和s[r]
相等,说明当前首尾字符相同,将l
加一,r
减一,继续检查下一对字符。如果s[l]
和s[r]
不相等,说明字符串不是回文串,输出N
并返回0
结束程序。如果循环正常结束,说明字符串是回文串,输出Y
并返回0
结束程序。
题目二十三
原题链接:lanqiao1372
题目
5 6
1 2 3 4 5
2
** 运行限制**
- 最大运行时间:1s
- 最大运行内存: 128M
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+9;
int a[N],S;
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n,s;cin>>n>>s;
for(int i=1;i<=n;i++)cin>>a[i];
int ans=n+1;
for(int i=1,j=0,sum=0;i<=n;i++){
while(i>j||(j+1<=n&&sum<s))sum+=a[++j];
if(sum>=s)ans=min(ans,j-i+1);
sum-=a[i];
}
cout<<(ans>n?0:ans)<<endl;
return 0;
}
题解分析
本题使用快慢指针求解。
-
在
main
函数中,首先使用ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
来同步输入输出流,提高输入输出效率。然后读取两个整数n
和s
。接着通过循环读取n
个整数存入数组a
。 -
定义变量
ans
初始值为n + 1
。然后通过两个指针i
和j
以及变量sum
来实现一个滑动窗口的功能。内层循环在满足一定条件下增加j
,直到窗口内的和大于等于s
或者j
达到边界。如果窗口内的和大于等于s
,则更新ans
为最小的窗口长度。然后在每次外层循环中,将sum
减去a[i]
,移动窗口的左边界。
题目二十四
原题链接:lanqiao1331
题目
代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
unsigned int x;cin>>x;
int ans=0;
while(x){
if(x+;
x>>=1;
}
cout<<ans<<endl;
return 0;
}
题解分析
本题需要用到位运算相关知识。
-
x&1
表示使用按位与操作符&
检查x
的最低位是否为1
。如果x & 1
的结果为1
,说明x
的最低位是1
,此时将ans
加1
。 -
x >>= 1
表示将x
右移一位,相当于去掉x
的最低位,继续检查下一个最低位。
题目二十五
原题链接:lanqiao3400
题目
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+9;
int a[N],prexor[N],cnt[N];
int main()
{
int n;cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)prexor[i]=prexor[i-1]^a[i];
cnt[0]=1;
int ans=n*(n+1)/2;
for(int i=1;i<=n;i++){
for(int j=0;j<=200;j++){
int sq=j*j;
ans-=cnt[prexor[i]^sq];
}
cnt[prexor[i]]++;
}
cout<<ans<<'\n';
return 0;
}
题解分析
-
首先转化条件:因数个数为偶数个=>不是完全平方数。
-
正难则反:计算有多少个子数组的异或和为完全平方数,用总区间个数减去即可。
-
先求前缀异或和
prexor
,枚举所有平方数sq
(不超过200个),然后对于prexor[i]
,要有prexor[i] ^ prexor[i]= sq
,于是就是统计多少个i
满足j<i
且,prexor[j]=prexor[i] ^ sq
。 -
总区间个数为
n* (n + 1)/ 2
,且元素异或和最大值不超过2e4
。