目录
因为本人使用AcWing比较多,所以这里的题目链接全都在AcWing上,如果大家想更系统的去查看这些题目,大家可以前往C语言网查找原题
纸张尺寸
很简单的一道题目,直接进行循环判断即可
代码如下:
#include<iostream>
using namespace std;
int main()
{
string arr;
cin>>arr;
int k=arr[1]-'0';
int a=1189,b=841;
while(k--)
{
swap(a,b);
b/=2;
}
cout<<a<<endl<<b<<endl;
}
求和
原题链接:4644. 求和 - AcWing题库
这道题先看一眼发现数据个数在2e6,首先排除两重暴力循环,那么就要思考其他解法,发现,我们要求的是两两相乘再相加,但是对于某一个数来说,就是他和除了他本身的其他所有数相乘再相加,那么基于此,我们可以直接优化掉一重,首先求出所有数的和,接着直接循环一遍数组,用和减去ai再乘以ai,相加即可
代码如下:
#include<iostream>
using namespace std;
typedef long long LL;
int arr[200010];
int main()
{
int n;
cin>>n;
LL ans=0;
for(int i=1;i<=n;i++)
{
cin>>arr[i];
ans+=arr[i];
}
LL cnt=0;
for(int i=1;i<=n;i++)
{
cnt+=arr[i]*(ans-arr[i]);
ans-=arr[i];
}
cout<<cnt<<endl;
}
数位排序
在各个数位和相同的情况下,我们需要选出最小的数,让他排在最前面,所以这道题目的思路就清晰了,我们直接开个结构体存一下数字和他的各数位和,然后进行结构体排序即可;
很简单的结构体排序题目,1e6很轻松就可以过掉,sort可以快速排完;
代码如下:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct ST
{
int in,he;//数,总和,位数
}arr[1000010];
void pd(int p)
{
int k=p;
int a=0;
while(k!=0)
{
a+=k%10;
k/=10;
}
arr[p].he=a;
}
int cmp(ST a,ST b)
{
if(a.he!=b.he)
return a.he<b.he;
return a.in<b.in;
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
arr[i].in=i;
pd(i);
}
sort(arr+1,arr+1+n,cmp);
// for(int i=1;i<=n;i++)
// cout<<arr[i].he<<' ';
cout<<arr[m].in<<endl;
}
选取异或
首先,肯定不能直接暴力,暴力绝对会超时,那么我们就要想其他办法,我们很容易想到,直接遍历原来的数组,我们直接查找谁和他异或,加入到他里面就行,但是这样也有一个问题,循环次数还是n方,而且接下来查询虽然快,但第二步遍历查找很容易被卡死,这个我没试过,大家可以试一试;
第三个方法是,我们在这个数对应的异或数中加入这个数的坐标,这样我们就把位置的判断转换成了数的判断,而且这样写的话第一步是n的复杂度;
于是我们就可以写下这个代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<set>
using namespace std;
int n,m,l,r,k;
int a[100010];
set<int> s[2000003];
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[a[i]^k].insert(i);//把与这个位置的数异或的结果加上这个位置
}
for(int i=1;i<=m;i++)
{
cin>>l>>r;
int flag=0;
for(int j=l;j<=r&&flag==0;j++)//从l到r之间进行循环
{
int A=a[j];
if(s[A].size()==0) continue;//如果s这个位置为空,说明没有任何一个数异或得到这个数,可以直接退出
for(set<int>::iterator it=s[A].begin();it!=s[A].end();it++)
{
if(l<=*it&&*it<=r)//如果找到这个数他的异或结果有位置在l到r之间,直接退出
{
flag=1;
break;
}
else if(*it>r) break;//优化,大于r说明已经找不到了,直接退出
}
}
if(flag==0) cout<<"no"<<endl;
else cout<<"yes"<<endl;
}
}
消除游戏
本来以为这是一道很简单的题目,想着直接开个栈存一下进行判断就行,后来才发现,人家要每一次删一遍,删完再删,如果用栈的话他会一直删,于是就放弃了栈的想法,使用了暴力模拟,但是暴力模拟也有数据过不掉,我这里实在是没招了,期待大家的更好的代码:
#include<iostream>
using namespace std;
int main()
{
string a,b;
cin>>a;
while(1)
{
int flag=0;//flag的含义是,如果遇到两种情况,他们后面的字符是都不加进去的,我们要特判消除
for(int i=0;i<a.size();i++)
{
if(flag)
flag--;
if(a[i]!=a[i+1]&&a[i+1]==a[i+2]&&i+2<a.size())//第一种情况,前不等后等,那个会有两个字符不加进去
{
flag=2;
continue;
}
else if(a[i]==a[i+1]&&a[i+1]!=a[i+2]&&i+2<a.size())//前等后不等,会有三个加不进去,这时候可能存在加入的情况
{
if(!flag)//特判这步加入的情况
b+=a[i];
flag=3;
continue;
}
if(!flag)//如果没被标记,就加入
b+=a[i];
}
if(b.size()==a.size()) break;//出现没有消除的情况,说明已经消除完成
else if(b.size()==0)//发现为空了,直接退出
{
cout<<"EMPTY";
break;
}
a=b;
b="";
}
if(b.size()) cout<<a<<endl;
}
重新排序
这道题目有两种做法,第一种是,因为我们只需要管谁出现次数多,谁出现次数少,所以可以直接进行排序,把每个数的出现次数进行排序,然后把原数排序,然后直接进行对应相乘,加起来减去原来的结果即可,但是这样做有一个问题,这个数的大小是超LL的,我们只能先减去再进行乘法;
于是我们可以在排序出现次数时,把原数组位置随之改变,这样再开一个数组进行排序,每一次相乘使用一个差值相乘,这样就可以避免超出LL;
代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
int n,m;
int ae[100010];
struct ST
{
int a;
int b;
}arr[100010];
int cmp(ST x,ST y)
{
return x.b<y.b;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d",&arr[i].a);
ae[i]=arr[i].a;//额外存储;
}
cin>>m;
while(m--)
{
int x,y;
scanf("%d%d",&x,&y);
arr[x].b++,arr[y+1].b--;//使用前缀和优化
}
for(int i=2;i<=n;i++) arr[i].b+=arr[i-1].b;
sort(ae+1,ae+1+n);
sort(arr+1,arr+1+n,cmp);//自定义排序
// for(int i=1;i<=n;i++)
// cout<<ae[i]<<' '<<arr[i].a<<' '<<arr[i].b<<endl;
LL cnt=0;
for(int i=n;i>=1;i--) cnt+=(LL)(ae[i]-arr[i].a)*arr[i].b;//使用差值可以避免超出LL
cout<<cnt<<endl;
}
只因能升级
首先就发现m太大了,我们要向计算技能,一种方法就是把所有技能加点情况全列出来,然后进行排序,取前m个即可,但是m在int尽头是肯定的存不下的,而且排序也会超时;
这里有个很经典转换,我们发现技能加点在1e6之间,我们能不能想办法,把每个技能加点都存在数据范围中,这样就可以存下所有情况,只需要遍历1e6,把m减完即可;
但是这样有个问题,会超时,1e5的数据大小为1e6,差值为1就是1e11,肯定超时,所以这个解法看看就行了,暴力骗点分。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
LL arr[1000010];
int main()
{
LL n,m;
cin>>n>>m;
while(n--)
{
int a,b;
scanf("%d%d",&a,&b);
while(a>0)
{
arr[a]++;//如果a出现了一次,就说明可以加点一次
a-=b;
}
}
// for(int i=1;i<=10;i++) cout<<i<<' '<<arr[i]<<endl;
LL ans=0;
for(int i=1000000;i>=1;i--)//只需要遍历所有加技能点的情况即可
{
if(arr[i]&&m>=arr[i])
{
ans+=(LL)arr[i]*i;
m-=arr[i];
}
else if(arr[i]&&m<arr[i])//判断m不够加点的情况
{
ans+=(LL)m*i;
m=0;
}
if(m==0)
break;
}
cout<<ans<<endl;
}
重复的数
看着答案说是离线莫队,我也不会,这算是算法进阶的难度,大家有兴趣可以自己去学;
暴力写法也比较简单,这里就不展示代码了