复盘7.8训练
Posted karshey
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了复盘7.8训练相关的知识,希望对你有一定的参考价值。
手感奇差,WA了贼多发的一次训练。
A-Pretty Permutations
原题
简单思维题,观察可得:偶数两两交换,奇数最后一个跟交换过的倒数第二个再交换可得。
注意
不要被样例迷惑了,不是要顺逆时针的旋转!
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[105];
int main()
{
int t;cin>>t;
while(t--)
{
memset(a,0,sizeof(a));
int n;cin>>n;
for(int i=1;i<=n;i++)
{
a[i]=i;
}
if(n%2==0) //偶数
{
for(int i=1;i<=n;i=i+2)
{
swap(a[i],a[i+1]);
}
}
else //奇数
{
for(int i=1;i<n;i=i+2)
{
swap(a[i],a[i+1]);
}
swap(a[n],a[n-1]);
}
for(int i=1;i<=n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
return 0;
}
B - Pleasant Pairs
要求存在的i+j==a[i]*a[j]的个数。
注意:直接双层循环肯定会T。所以要想办法剪枝。(因为其实符合题意的并不多,如果每一个都判断一次就会复杂度很高,所以要把先符合要求的筛选出来再判断)
a[i]*a[j]=i+j;
由于a[j]最小为1,则有:n>=j>=a[i]-i;
由题意可得,j每次是a[i]倍增加的(a[i]*a[j])
故有:
for(int j=a[i]-i;j<=n;j=j+a[i])
最后注意j要大于i判断一下,开ll即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll a[N];
int main()
{
int t;cin>>t;
while(t--)
{
memset(a,0,sizeof(a));
int n;cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
ll ans=0;
for(int i=1;i<=n;i++)
{
for(int j=a[i]-i;j<=n;j=j+a[i])
{
if(j<=i) continue;
if(a[i]*a[j]==i+j) ans++;
}
}
cout<<ans<<endl;
}
return 0;
}
C - Colorful Transceivers
原题
超级无敌大水题了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
// ab bc直接 ac 间接
//ac 可通 yes
int main()
{
int a,b,c,d;cin>>a>>b>>c>>d;
if(abs(a-c)<=d) cout<<"Yes"<<endl;
else
{
if(abs(a-b)<=d&&abs(b-c)<=d) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return 0;
}
D - Exponential
原题
注意数据范围小,才1000,这妥妥的可以打表!
做的时候没想到,结果直接T了贼多发
打表过程见代码。打完表还WA,竟是因为…忘记可以等于了…
注意,不用去重。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int cmp(int a,int b)
{
return a>b;
}
int main()
{
int x;cin>>x;
if(x<=3) {
cout<<1;return 0;
}
//打表:2次方
int p=0,a[1005];
for(int i=2;i<=31;i++) //32^2>1000
{
a[p++]=pow(i,2);
}
//3
for(int i=2;i<=10;i++) //10^3==1000
{
a[p++]=pow(i,3);
}
//5
for(int i=2;i<=3;i++) //4^5>1000
{
a[p++]=pow(i,5);
}
//7
a[p++]=pow(2,7); //3^7>1000
//2^11>1000 到11打表结束
sort(a,a+p);
//
// for(int i=0;i<p;i++)
// {
// cout<<a[i]<<endl;
// }
//
for(int i=p-1;i>=0;i--)
{
if(a[i]<=x)
{
cout<<a[i];
return 0;
}
}
return 0;
}
E- A - B = C
原题
大意:输入L,R,在LR中有多少ABC在LR范围内满足A-B=C;观察可得,等差数列。
看样例,有提示。
这题真的见识到了cin和cout会T,必须用printf和scanf。
ps:第一次WA的时候判断了输入的LR相同等等之类的情况,其实可以被这里idx<1的判断一言以蔽之。可能是我多加了判断但判断的不全面。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int t;cin>>t;
while(t--)
{
int a,b;scanf("%d%d",&a,&b);
ll idx=(b-2*a+1);
ll ans=0;
if(idx<1)
{
printf("0\\n");
}
else
{
ans=(1+idx)*idx;
ans/=2;
printf("%lld\\n",ans);
}
}
return 0;
}
F - K-th Substring
原题
求出第k个字典序最小的字串。
注意:k<=5的,如果所有字串都求出来会T,也要有剪枝思想。所以循环的时候内层循环1~5即可。
STL的set可以自动排序和去重,用它做容器。(第一次A的时候竟然手动排序和去重,傻了)
取字符子串的用substr,当然也可以自己加。
两个循环:外层是0-len-1,内层是1-5的闭区间。注意判断是否超出长度。
for(int i=0;i<=len-1;i++)
for(int j=1;j<=5;j++)
{
if(i+j-1>=len) continue;
s.insert(a.substr(i,j));
}
见代码:
set版:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
string a;cin>>a;
int k;cin>>k;
int len=a.size();
set<string>s;
for(int i=0;i<=len-1;i++)
{
for(int j=1;j<=5;j++)
{
if(i+j-1>=len) continue;
s.insert(a.substr(i,j));
}
}
set<string>::iterator iter;
int temp=1;
for(iter=s.begin();iter!=s.end();iter++)
{
if(temp==k){cout<<*iter<<endl;return 0;
}
temp++;
}
// for(iter=s.begin();iter!=s.end();iter++)
// {
// cout<<*iter<<endl;
// }
return 0;
}
非set版:
字典序:
(符号竟是试出来的)
int cmp(string a,string b)
{
if(a.size()!=b.size()) return a<b;
else return a+b<b+a;
}
总代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int cmp(string a,string b)
{
if(a.size()!=b.size()) return a<b;
else return a+b<b+a;
}
int main()
{
string a;cin>>a;
int k;cin>>k;
int len=a.size();
int k1=min(k,len);
string str[N];
int sum=0;
for(int i=0;a[i];i++)
{
string c;
for(int j=i;j<=i+k1&&a[j];j++)
{
c+=a[j];
str[sum++]=c;
}
}
sort(str,str+sum,cmp);
unique(str,str+sum);
int sum1=0;
// for(int i=0;i<sum;i++)
// {
// cout<<str[i]<<endl;
// }
cout<<str[k-1];
return 0;
}
ps:如果用string c自己加一个string出来,竟然无法用set去重,有点疑惑。
G - Attention
原题
大意:一排人面向西边或东边。选一个人做leader,其他人都要面向他。求转向最少的。
即leader左边的人要为E,右边的人要为W。
暴力T了一发。然后想到用前缀和。
w代表此人左边的W,e代表此人右边的E。ww代表此人是W,ee代表此人是E。
求出每一个leader左边所有E和右边所有W的最大值,即得到要转身的最小值。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+5;
struct node
{
char op;
int w=0,e=0;
int ww=0,ee=0;
}a[N];
//左E右W
int main()
{
int n;cin>>n;
for(int i=1;i<=n;i++)
{
char op;cin>>op;
a[i].op=op;
if(op=='W') a[i].ww++;
else a[i].ee++;
a[i].e+=a[i-1].e+a[i-1].ee;
a[i].w+=a[i-1].w+a[i-1].ww;
}
if(a[n].ww) a[n].w++;
int sum=0,ans=0;
for(int i=1;i<=n;i++)
{
int tempw;
if(a[i].op=='W') tempw=a[n].w-a[i].w-1;
else tempw=a[n].w-a[i].w;
if(tempw<0) tempw=0;
sum=tempw+a[i].e;
ans=max(ans,sum);
}
ans=n-ans-1;
if(ans<0) ans=0;
cout<<ans;
return 0;
}
以上是关于复盘7.8训练的主要内容,如果未能解决你的问题,请参考以下文章
复盘8.17训练:UCF Local Programming Contest Final Round