思路:
分两种情况:
1.除了最大的数 其他的都是与最大的数的和最大
2.最大的数与第二大的数的和最大
代码实现
法一:利用pair的对应赋值关系实现
#include
using namespace std;
const int M=1e5+10;
typedef pair<int,int> PII;
PII a[M];
ll res[N];
int main()
{
ll n;
cin>>n;
for(ll i=1;i<=n;i++){
cin>>a[i].first;
a[i].second=i;
}
sort(a+1,a+1+n);
for(ll i=1;i<=n;i++){
int id=a[i].second;
if(i!=n){
res[id]=a[i].first+a[n].first;
}
else {
res[id]=a[i].first+a[i-1].first;
}
}
for(int i=1;i<=n;i++){
cout<<res[i]<<" ";
}
}
法二:利用数组赋值实现
#include
using namespace std;
int main()
{long long n,i,j,max,k,sum;
cin>>n;
int a[n],b[n];
for(i=0;i<n;i++)
cin>>a[i];
for(i=0;i<n;i++)
b[i]=a[i];
sort(a,a+n);
for(i=0;i<n;i++)
if(b[i]!=a[n-1])
cout<<b[i]+a[n-1]<<" ";
else
cout<<a[n-2]+a[n-1]<<" ";
}
一题多解 多重标记的多重考虑 数组/pair 标记 维护序列的顺序
再逐一输出
快速幂的典型板子题 注意ll 不要溢出了
小心细节 小心驶得万年船
#include
using namespace std;
typedef long long ll;
ll qmi(ll a,ll b,ll p){
ll res=1;
while(b){
if(b&1)res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
int main(){
ll n;
cin>>n;
ll m=qmi(n,n,n+2);
printf("%lld\n",m);
return 0;
}
思路:
先接收一个字符串 再把小字符串和数字分开
用map装住 再询问
注意!!!!
由于字符串和整型int的区别 电脑无法区分两者 所以不能多次快速接收
一定要手动分开!!!
有两种区分方法:
法一:字符串函数
知识点1:! ! stringstream
cin/s stringstream ss(str); //定义一个stringstream类型的对象并用str进行初始化
while(ss >> x) //可以理解为cin >> , cin也是以空格或者回车作为结束的标准
//实际上cin应该也是类似于这种输入,都是通过流来输入的。
代码实现:
#include
using namespace std;
map<string,int> mp;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
string ss;//先把全部的对看成一个字符串
getline(cin,ss);//再进行分割
stringstream sin;
sin<<ss;//将ss转变成输入流
int k;
string a;
while(sin>>a>>k){
mp[a]+=k;//有可能有重复的a
}
int t;
cin>>t;
while(t--){
string s;
cin>>s;
cout<<mp[s]<<endl;
}
}
总结/易错点:
cin或定义的stringstream只能根据空格或换行分割
如用其他字符进行分割的 必须先进行转换(作为字符数组/字符串 接收进来进行循环转换)
法二:自行分割后输入
#include
using namespace std;
map<string,int> mp;
int main(){
char c;
int n;
string s;
cin>>s>>n;//正常接收字符串 遇到整型就停止了
scanf("%c",&c);//接收了个空格
while(1){
if(c==10)break;// \n的ascii码为10
cin>>s>>n;
scanf("%c",&c);
if(mp[s])mp[s]+=n;
else mp[s]=n;
}
int t;
cin>>t;
while(t--){
string s;
cin>>s;
cout<<mp[s]<<endl;
}
}
总结:
最笨的方法
一个个输入 将输入的数据分为 字符串 整型 字符
再不断循环(至少先接收一次)
典型的数学题
思路:找规律 二进制
思路:
#include
using namespace std;
typedef pair<int,int> PII;
const int N=310;
int n,m,k,a,b,c,d;
int g[N][N];
int dx[4]={-2,-2,-1,1,-1,1,2,2},dy[4]={-1,1,-2,-2,2,2,-1,1};
int dist[N][N],dis[N][N];//一个国际 一个中国
void bfs(){
queue<PII>heap;
heap.push({a1,b});//把起点坐标加入队列
while(heap.size())//当队列不为空
{
auto tt=heap.front();
int x=tt.first,y=tt.second;
if(x==c&&y==d)return;//到达终点
heap.pop();//把队头删掉
for(int i=0;i<8;i++)//八个方向不断遍历
{
int l=x+dx[i],r=y+dy[i];
if(l<1||l>n||r<1||r>m)continue;
int k=i/2;
if(k==0&&(a[x-1][y] + a[l][r])) //再加入4个特判
if(dist[l][r]>dist[x][y]+1)
{
dist[l][r]=dist[x][y]+1;
heap.push({l,r});//l,r 加入队列
}
}
}
}
void bfs1(){
queue<PII>heap;
heap.push({a1,b});//把起点坐标加入队列
while(heap.size())//当队列不为空
{
auto tt=heap.front();
int x=tt.first,y=tt.second;
if(x==c&&y==d)return;//到达终点
heap.pop();//把队头删掉
for(int i=0;i<8;i++)//八个方向不断遍历
{
int l=x+dx[i],r=y+dy[i];
if(l<1||l>n||r<1||r>m)continue;
if(dist[l][r]>dist[x][y]+1){
dist[l][r]=dist[x][y]+1;
heap.push({l,r});//l,r 加入队列
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
while(t--){
cin>>n>>m>>k>>a1>>b>>c>>d;
for(int i=0;i<k;i++){
int x,y;
cin>>x>>y;
a[x][y]=1;//记录此地方不能走
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
dist[i][j]=dis[i][j]=1e9;}
dist[a1][b]=dis[a1][b]=0;
if(a[a1][b]+a[c][d])//起点和终点都没有棋子
{
printf("-1 -1\n");
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j]=0;
continue;
}
bfs();
bfs1();
if(dis[c][d]<1e9)printf("%d",dis[c][d]);
if(dist[c][d]<1e9)printf("%d",dist[c][d]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j]=0;
}
}
#include
using namespace std;
typedef long long ll;
const int N=1e6+10;
int a[N];//用于存储前i天有多少天是晴天
char ss[N];
int main(){
ll n,x,y;
cin>>n>>x>>y;
scanf("%s",ss+1);//表示从下标为1的数组开始记录
for(int i=1;i<=n;i++){
a[i]=a[i-1];
if(ss[i]=='0')a[i]++;
}
ll res=0;
for(int i=1;i<=n;i++){
int l=i,r=n;
while(l<r){
ll mid=l+r>>1;
ll rest=a[mid]-a[i-1];
if(rest>=x&&(mid-i+1)-rest>=y)r=mid;
else l=mid+1;
}
ll rest=a[r]-a[i-1];
ll p=(r-i+1)=rest;
if(rest>=x&&p>=y)
res+=(n-r+1);
}
if(x+y==0)res+=1;
cout<<res;
}
int dp[N][N];//最简单的01背包问题
int h[N];
int main()
{
int n,k;
cin>>n>>k;
memset(dp,0x3f,sizeof dp);
dp[0][0]=0;
for(int i=1;i<=n;i++){
cin>>h[i];
dp[i][0]=0;
// dp[i][j]表示的是从前i个里面选高度为j的堆数量
}
sort(h+1,h+1+n);
for(int i=1;i<=n;i++){
for(int j=1;j<=3000;j++)//测试点的最大高为3000
dp[i][j] = dp[i-1][j];
if(j>=h[i]){
dp[i][j]=min(dp[i-1][j],dp[i-1][j-h[i]]+1);
}
}
//计算过程结束
//接下来判断结果 特例
if(dp[n][k]==0x3f3f3f3f){
cout<<"-1";
return 0;
}
cout<<dp[n][k]<<endl;
vector<int>res;
for(int i=n;i>=1;i--)//h已经按照升序排序 则需要从n到1遍历
//此过程为 倒着找硬币堆 并拉入stl
{
if(k>=h[i]&&dp[i-1][k-h[i]]+1==dp[n][k]){
res.push_back(h[i]);
k-=h[i];//已知到答案 一步步倒推每个元素
}
}
sort(res.begin(),res.end());//多解则按字典序排列
for(int i=0;i<res.size();i++)//遍历每个答案
{
cout<<res[i]<<" ";
}
}
思路:利用数学公式
学会巧妙的用o(1)的算法
先分区间 在进行公式套用
#include"bits/stdc++.h"
using namespace std;
typedef long long ll;
const int mod=998244353;
const int inv2=(998244353+1)/2;
ll n;
int main(){
cin>>n;
ll ans=(n % mod)*(n % mod) %mod;
for(ll i=1,r;i<=n;i=r+1){
r=n/(n/i);//相同商的右端点
ans-=(n/i)%mod*((r+i)%mod)%mod*((r+1-i)%mod)%mod*inv2%mod;
ans=(ans%mod+mod)%mod;
}
cout<<ans<<'\n';
return 0;
}
总结:
本次题解就到这里啦!!我们下一次赛后见~
每次回顾 写题解是我最快乐的时光嘿嘿~~