湖南师大附中OI训练总结
hnsdfzoj 训练
首先声明,水题不首A八百字检讨+重做10遍(有点过了)
否则直接过真的不是我不重视
不知为何,我不喜欢万能头
1001: 最佳方案(本题首A)
题目传送门
我们的目的是要将n变成负数,我们已知n/=2是不可能把n降到0一下,所以必须通过n/=2降到能通过n-=x<0的程度
于是就有了如下代码
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
int n,x,t;
int main(){
cin>>t;
for(int i=0;i<t;i++){
cin>>n>>x;
int sum=0;
while(n>=0){
if(n-x<0){
sum+=1;
break;
}
n=min(n/2,n-x);
sum+=1;
}
cout<<sum<<"\n";
}
return 0;
}
完成!
1002: 回文串(本题首A)
题目传送门
本题我们可以先记录不匹配的数量,然后通过输入更改字符串。如果原来匹配现在不匹配,就把不匹配数量+=1;如果原来不匹配现在匹配且不匹配数>0,就把不匹配数–=1;如果原来匹配,现在也匹配,不匹配数不变。
于是就有了如下代码
#include<cstdio>
#include<iostream>
#include<cmath>
#include<string>
using namespace std;
string s,tmp;
int n,sum;
int main(){
cin>>s>>n;
for(int i=0,j=s.size()-1;i<=j;i++,j--){
if(s[i]!=s[j])sum++;
}
int len=s.size();
for(int i=0;i<n;i++){
int p;
char w;
cin>>p>>w;
p--;
if(s[p]!=s[len-p-1]){
if(s[len-p-1]==w)sum--;
}
else{
if(len%2==1&&p==len/2);
else if(s[len-p-1]!=w)sum++;
}
if(sum>0)cout<<"No\n";
else cout<<"Yes\n";
s[p]=w;
}
return 0;
}
完成!
1004: 壁纸(本题3A)
题目传送门
本题真的是一道好题
首先明确一种物极必反 正难则反的思想
意思就是 正着走太麻烦就从后往前走
从前往后的话,每天要看一下前面有没有壁纸可换。复杂度O(n^2)
再看看数据 n<=1e5
n^2<=1e10
显然会TLE (没有数据范围的题目永远是水题 )
首次提交,时间爆炸
二话不说,就来优化
于是就用到了正难则反的思想
正的玩不起就来反的!
于是,我们从后往前的第i天发现今天换了第i天的壁纸后可以用到整个n天结束,就表示今后不能再换,于是dp[i]=1; 但是(没错,我就是那个意外)如果第i天换了壁纸并且后面还可以换壁纸,dp[i]就应该加上后面所有可能的总和
那这个总和怎么算呢?
众所周知,一个东西叫做前缀和
这里我们也是求他的兄弟后缀和!(真兄弟)
因为前面说了dp数组是用来存第i天后(包括第i天)还可以换几次壁纸,于是我们就是求dp的后缀和啦!
于是就有了如下代码
#include<cstdio>
#include<iostream>
#include<cmath>
#include<string>
using namespace std;
const int MAXN=1e5+10;
int n;
long long a[MAXN];
long long dp[MAXN];
long long sum[MAXN];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=n;i>=1;i--){
if(i+a[i]>n)dp[i]=1;
if(i+a[i]<=n){
dp[i]=1+sum[i+a[i]];
}
sum[i]=(sum[i+1]%1000000007+dp[i]%1000000007)%1000000007;
}
cout<<sum[1]%1000000007;
return 0;
}
忘了一件大事(你以为就完了吗?)
一定要%1000000007!!!
一定要%1000000007!!!
一定要%1000000007!!!
重要的事情说三遍
完成!
1005: 智力测试题
题目传送门
这题没有首A纯属失误(真的不是我轻视)
除法没用浮点型,并且把t%=(n*n);t++;搞反了(QAQ)
这告诉了我们先++在运算会逝(死的很惨)
直接上代码
Code:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<string>
using namespace std;
int n,t;
int main(){
cin>>n>>t;
t%=(n*n);t++;
int h,l;
h=t/n+(t%n!=0);
if(t%n==0)l=n;
else l=t%n;
cout<<h<<" "<<l;
return 0;
}
完成!
1007: 飞翔的排列
(说实话这题真的很坑)
因为我们可以发现,不论循环到哪一次总会找到最后一个,所以说不管如何最多也只能飞翔一次
但是(没错,我就是那个意外)
如果最后一个是整个排列的最小值(即为单调递减序列),不能飞翔
(所以说只有1和0两种答案)很坑不是吗?
直接输出1得60分
直接输出0得40分
于是就有了如下代码
#include<cstdio>
#include<iostream>
using namespace std;
int n;
int a;
int minn=0x3f3f3f3f;
int main(){
cin>>n;
for(int i=0;i<n;i++){
cin>>a;
if(a<minn){
minn=a;
}
}
if(minn==a)cout<<0;
else cout<<1;
return 0;
}
完成!
1009: 晒衣服与烘干机
这题细节真的超多!
先说几点
1、自然晾干与干衣机不可同时进行
2、干衣机是真的小,每次只能烘一件衣服
3、自然晾干每秒干1的水分,干衣机每秒干k的水分
OK开始吧!
首先看一下数据范围,n<=1e5
这下惨了,暴力O(n^2)过不了,于是
天空一声巨响,二分O(n*log(n))闪亮登场!
我们可以二分答案,即多久可以干掉,然后在check函数中判断这个时间内能否全部晾干,如果可以就记录答案并将时间缩小,如果不行就把时间放大
于是就有了如下代码
#include<iostream>
#include<cmath>
using namespace std;
const int MAXN=1e5+10;
int n,ans;
int a[MAXN];
int k;
int maxa=-0x3f3f3f3f;
bool check(int x){
long long sum=0;
for(int i=1;i<=n;i++){
if(a[i]>x){
sum+=ceil((a[i]-x)*1.0/(k-1));//注意,k-1可能为0,当k==1时需要特判
}
}
if(sum<=x)return 1;
else return 0;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]>maxa)maxa=a[i];//记录最大值,特判时需要
}
cin>>k;
if(k==1){//如果干衣机的烘干速度与晾干一样,直接输出最大值
cout<<maxa;
return 0;
}
int l=1,r=1e9;
while(l<=r){
int mid=l+(r-l)/2;
if(check(mid)){
ans=mid;
r=mid-1;
}else{
l=mid+1;
}
}
cout<<ans;
return 0;
}
完成!
1010: 找因数
题目传送门
看题过后,首先想到暴力
但是暴力O(t*n)
肯定过不了
但是,我们知道暴力的一种优化,即n的范围只要找到sqrt(n)即可
于是代码诞生!
#include<iostream>
#include<cmath>
using namespace std;
int t,n;
int main(){
cin>>t;
for(int i=0;i<t;i++){
bool flag=0;
cin>>n;
int len=sqrt(n);
for(int j=2;j<=len;j++){
if(n%j==0){
cout<<n/j<<"\n";
flag=1;
break;
}
}
if(flag==0)cout<<1<<"\n";
}
return 0;
}
完成!
1011: 赚金币
题目传送门
本题主要在于进制转换,其他没啥
但是,如果没开long long,就会逝得很惨(44944)
OK,来看一下代码
#include <cstdio>
#include <iostream>
using namespace std;
int t,n,d,k,x;
long long swp(int x,int k){
long long i=1,h=0;
while(x>0){
h+=(x%k)*i;
x/=k;
i*=10;
}
return h;
}
int main(){
cin>>t;
for(int i=0;i<t;i++){
int sum=0;
cin>>n>>d>>k>>x;
for(int j=n;j<n+d;j++){
long long tmp=swp(j,k);
// cout<<tmp<<" ";
while(tmp>0){
if(tmp%10==x)sum++;
tmp/=10;
}
}
cout<<sum<<"\n";
}
return 0;
}
完成!
1018: 咖啡牛奶
本题是我见到的一道二分好题
题目传送门
我们可以知道1份咖啡牛奶中必须包含以下两种条件。
-
4份咖啡或牛奶。
-
至少包含一份咖啡和牛奶。
我们就知道了如何验证是否可组成已知分数的咖啡牛奶