文章目录
问题 A: 牛牛数三角形(数论)
思路:
先求出所给目标点上面行的的总个数再加上本行的总个数对 10 取模即可,所给目标点上边行恰好是一个等差数列 , 求解前 n 项 和 ,注意要 开 ll , 并且边处理 便取模;
知识点 :1 取模规则:
一定一定注意除法是不能拆开取模的;!!!
知识点 2:数据范围
int 2e9
long long 9e18
知识点 3:
在等差数列前 n 项 和公式中 (首项+末项) 和 项数一定有一个是 2的倍数
可以借助这个性质防止爆 ll
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
typedef long long ll;
ll n,x,y,sum;//sum 记录上方行个数
const int p = 10;
int main()
{
cin>>n>>x>>y;//n 是首项
ll m=n-(x-2);//末项
ll k=x-1;//项数
if(x==1)
{
sum=0;
}//x==1 时无上方行
else
if(k%2==0)
{
k=(k/2)%p;//项数是 2 的倍数 ,先对项数除2
sum=(m%p+n%p)%p*k;
sum%=p;//再计算前 n 项和
}
else
{
sum=(n+m)/2%p;//如果 (首项+末项)是 2 的倍数,对这部分 ÷ 2 ,一定注意 不要对 n 和 m 取余 ,因为后面要 除 2 ,出发不满足连续取模的性质!!!!!!!!!!!
sum=(sum*k)%p;
}
sum=(sum+y-1)%p;//位置从 0 开始 ,最后要 减 1
cout<<sum;
}
问题 B: 牛牛的四则运算
思路:
搜索两遍,第一遍记下乘除的顺序,第二遍记下加减的顺序,然后输出即可
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
typedef long long ll;
int n;
string s;
int len,cnt;
int a[N];
int main()
{
cin>>len;
cin>>s;
for(int i=0;i<len;i++)
{
if(s[i]=='*'||s[i]=='/')
{
a[i]=++cnt;
}
}
for(int i=0;i<len;i++)
{
if(s[i]=='+'||s[i]=='-')
{
a[i]=++cnt;
}
}
for(int i=0;i<len;i++)
{
if(s[i]!='+'&&s[i]!='-'&&s[i]!='*'&&s[i]!='/')
{
cout<<s[i];
}
else
{
cout<<s[i];
cout<<"["<<a[i]<<"]";
}
}
问题 E: X特工的加密情报(差分,前缀和)
思路:
操作 m 次, 每次对 一个区间加 1,这不就是差分前缀和的操作吗
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
typedef long long ll;
string s;
int cf[N];
int n,a,b,sum;
int main()
{
cin>>s;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a>>b;
cf[a-1]++;
cf[b]--;
}
//由于字符串下标 0 开始 ,所以差分数组也要对应左移一位
for(int i=0;i<(int)(s.size());i++)
{
sum=sum+cf[i];
int k=(s[i]-'a'+1+sum)%26;
if(k==0) k=26;
cout<<(char)(k+'a'-1);
}
}
问题 L: Adjacent Squares
大意:
给出一个 n * m 的矩形 ,每个点都是一个小正方形 ,给出其中一个小正方型的坐标 , 问其相邻的小正方形有几个(相邻就是上下左右)
思路:
搜索所给位置上下左右四个点是否越界即可
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
typedef long long ll;
int n,m;
int d1,d2,cnt;
int main()
{
cin>>n>>m;
cin>>d1>>d2;
if(d1-1>=1) cnt++;
if(d1+1<=n) cnt++;
if(d2-1>=1) cnt++;
if(d2+1<=m) cnt++;
cout<<cnt<<endl;
}
问题 M: Enlarged Checker Board
大意:
构建一个 a*b 的由点组成的矩形 ,然后以这个矩形为单位,构造 n 行 n 列 这样的矩形 ,要求是相邻矩形组成不相同,即点矩形旁边都是 #矩形 #矩形旁边都是点矩形
思路;
自己模拟算了,没什么具体思路;
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
typedef long long ll;
int n,a,b;
string s1,s2;
int flag;
int main()
{
cin>>n>>a>>b;
for(int i=1;i<=b;i++)
{
s1+='.';
s2+='#';
}
for(int k=1;k<=n;k++)
{
for(int i=1;i<=a;i++)
{
if(k%2!=0)
flag=-1;
else
flag=1;
for(int j=1;j<=n;j++)
{
if(flag<0) cout<<s1;
else cout<<s2;
flag=-flag;
}
cout<<endl;
}
}
}
问题 N: Adjacent Swaps(模拟交换)
大意:
一开始有n个数 ,从 1 - n 排列 ,每次询问找到 数 i 与其右边的数交换, 最右侧的数与其左侧数交换;输出交换完后的序列
思路:
数据范围比较大,我们找一个数,一个不能在数组里搜索这个数,太慢了,我们可以一开始记住这个数的位置,每次移动一次把记录的位置也改变,直接通过位置来访问数就能优化到O(1) 的复杂度了;
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2E5+10;
int n,q,k;
int a[N];
int id[N];
int main()
{
cin>>n>>q;
for(int i=1;i<=n;i++) a[i]=i,id[i]=i;
//一开始每个数的位置就是其下标
for(int i=1;i<=q;i++)
{
cin>>k;
if(id[k]!=n)
{
int idd=id[k];//找到要询问数的位置
int ss=a[idd+1];//找到下一个数
swap(a[idd],a[idd+1]);//交换两个数
swap(id[k],id[ss]);//交换两个数的位置
}
else
{//询问最后一个数时注意和左侧数交换
int idd=id[k];
int ss=a[idd-1];
swap(a[idd],a[idd-1]);
swap(id[k],id[ss]);
}
}
for(int i=1;i<=n;i++) cout<<a[i]<<" ";
}
问题 O: 250-like Number
大意:
给出一个数 n 要求 找出任意两个素数(p,q) 使得 p ∗ q 3 < = n p*q^3<= n p∗q3<=n ,问这样的素数对有多少;
思路:
没优化,欧拉筛+暴力枚举
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
//ios::sync_with_stdio(false);
const ll N = 1e8+10;
bool as[N+1];
int prime[N+1];
ll k,n,t,cnt,cnts;
void isprime()
{
as[0]=1;
as[1]=1;
for(int i=2;i<=N;i++)
{
if(!as[i]) prime[++cnt]=i;
for(int j=1;j<=cnt&&i*prime[j]<=N;j++)
{
as[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
}
int main()
{
isprime();
cin>>n;
for(int i=1;i<=cnt;i++)
{
ll a=prime[i],s=prime[i+1];
if(a*s*s*s>n) break;
for(int j=i+1;j<=cnt;j++)
{
ll b=prime[j];
// cout<<a*b*b*b<<endl;
if(a*b*b*b<=n) cnts++;
else break;
}
}
cout<<cnts;
}
优化思路:
只有大体思路,那就是算出需要枚举的素数范围,减少素数枚举个数;
问题 P: Prefix Equality(线性DP)
大意:
给出两个长度为 n 的序列,每次给出一个询问 p q ,问 a 的 前 p 个元素 和 b 的前 q 个元素 组成是否相同
思路:
我们记下每个元素最开始出现的位置,然后记下每个序列中前 i 个 元素 在 另一个序列中出现的最大位置,没出现记为无穷大 ,最后比较即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
const int inf = 0x3f3f3f3f;//无穷大
int n,q,x,y;
int a[N],b[N];
map<int,int>mp1;
map<int,int>mp2;
int ansa[N],ansb[N];
// ansa 对于 a 的前 n 个数,在 b 中出现的初始下标的最大值
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(!mp1.count(a[i])) mp1[a[i]]=i;
}
for(int i=1;i<=n;i++)
{
cin>>b[i];
if(!mp2.count(b[i])) mp2[b[i]]=i;
}
//记录每个元素首次出现的位置
for(int i=1;i<=n;i++)
{
ansa[i]=max(ansa[i-1],(mp2.count(a[i]) ? mp2[a[i]] : inf));
ansb[i]=max(ansb[i-1],(mp1.count(b[i]) ? mp1[b[i]] : inf));
}//记录每个序列前 i 个元素 在 另一个序列中出现的最大位置
cin>>q;
while(q--)
{
cin>>x>>y;
//当同时满足这两个条件时一定能保证数集相等,一旦一个里面有另一个不存在的元素就会出现无穷大,就不会满足条件
if(y>=ansa[x]&&x>=ansb[y])
{
puts("Yes");
}
else
{
puts("No");
}
}
}
反思:
这个题思路真的很巧妙,没有真的去比较每一个元素,而是记下了一个数列中元素在另一个数列中的位置的最大值,看给出的范围是否比最大值大,如果这个元素从来没有出现过的话就标记为无穷大,以后的对应数集不可能再组成相同,非常巧妙;
这个题思路值得反复思考!!!