基础练习(2)
1.Problem - C - Codeforces
很容易看出题目必须是一个等差数列。
考虑固定两个数,就可以求出整个数列,记录不相等的数即可。然后记录需要的最小次数。
因为直接求公差会出现误差,所以考虑直接
$$(a[i]-a[j])/(i-j)=(a[i]-a[k])/(i-k)
$$
对这个柿子边下形就好了。
/*
发现有种情况没有讨论
可能不是这个等差数列的数也是不需要改动的。
0,2,4,100 ,10只需要1次输出了2
那如何确定一个等差数列只需要固定两个数,然后就可以算出整个数列是什么了
*/
void slove(){
cin>>n;
fel(i,1,n) cin>>a[i];
int ans=n-1;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
int c=a[j]-a[i];
int d=j-i;
int cnt=0;
for(int k=1;k<=n;k++){
int u=a[k]-a[i];
int v=k-i;
if(c*v!=u*d) cnt++;
}
ans=min(ans,cnt);
}
}
cout<<ans<<endl;
}
2.Problem - 1707A - Codeforces
首先考虑如果能不能测试这一场,如果后面也有一场不能测试,那我肯定考虑在后面那一场再降智。这样才会一直保持较高的智商。才能取走更多东西。那如何实现先取后面的呢?考虑从后面取。这样才会优先取到后面的。
void slove(){
cin>>n>>q;
fel(i,1,n) cin>>a[i];
int cnt=0;
string s="";
feh(i,n,1){
if(cnt<a[i]&&cnt<q){
cnt++;
s+="1";
}
else if(cnt>=a[i]) s+='1';
else s+="0";
}
reverse(s.begin(),s.end());
cout<<s<<endl;
}
3.Problem - 1705C - Codeforces
感觉这种题目套路都差不多,就是考虑复制的时候我当前字符串在前面什么位置,然后考虑直递归解决即可。
这个题目我就直接考虑保存每一次复制之后字符串的长度和复制上一次字符串的区间。然后通过递归就可以知道当前位置的字符在上一次字符串里面是什么位置。
void slove(){
cin>>n>>c>>q;
cin>>s;
sum[0]=n;
for(int i=1;i<=c;i++){
cin>>l[i]>>r[i];
sum[i]=sum[i-1]+r[i]-l[i]+1;
}
for(int i=1,x;i<=q;i++){
cin>>x;
for(int j=c;j>=1;j--){
if(x<=sum[j]&&x>sum[j-1]){
x=l[j]+x-sum[j-1]-1;
}
}
cout<<s[x-1]<<endl;
}
}
4.Problem - C - Codeforces
就是对前面的字符尽量选择大的,然后如何判断这个字符可不可以选呢?
就是我后面的字符没有他,或者后面有25给字母绕个圈于是他就可以接在我的头上。
const int N=500;
int n;
string s;
int q[N],h[N],vis[N],cnt,flag;
void dfs(int now,int pre){
if(now==-1) return;
cnt++;
if(now==pre) flag=1;
dfs(h[now],pre);
}
bool check(char pre,char ne){
if(pre!=ne&&vis[pre]==0){//pre此时还没做过前驱,所以只要搜他在ne的后面没即可。
//两种可能一种是这个字符还没用过,另外一种是是ne和pre之间隔24个字母首位相接
cnt=0,flag=0;
dfs(h[ne],pre);
if(flag==0||cnt==25){
return true;
}
}
return false;
}
void slove(){
cin >> n;
cin >> s;
s = " " + s;
fel(i,0,200) vis[i]=0,q[i]=-1,h[i]=-1;
string ans="";
fel(i,1,n){
if(q[s[i]]!=-1){
ans+=q[s[i]];
continue;
}
for(char j='a';j<='z';j++){
if(check(j,s[i])){
q[s[i]]=j;
h[j]=s[i];
vis[j]=1;//后面有字符的给标记
ans+=j;
break;
}
}
}
cout<<ans<<endl;
}
5.Problem - D - Codeforces
这样的map确实也不错。
首先分析一下三张牌想要是good的话必须得每一项得和是三的倍数。000,111,222,012。总共就这四种情况。
让后再考虑五张牌必须组成两个集合。那么两个集合必然会重复一张牌。考虑是否可以证明两个集合最多只会共享一张牌。题目保证了向量不会重复。如果一个good集合中有两个向量相同那另外一个向量也必须是这个向量否则取模三是不可能等于0的,看上面的四种情况就知道。所以一个集合中三个向量都是不相同的。那考虑取1作为中心牌,后面如果2,3向量和2,4向量的和是相等的。那就会只选四个数。这种情况看似会出现。实际并不会,因为一个集合中如果两个向量相同第三个向量也必然相同,但是1,2,3和1,2,4并不全部相同所以这种情况并不会出现。
那么就可以考虑先算出两张牌的和。然后求每个牌作为重复的那张牌所产生mete--set。这样五个向量都是选择了不同的。
void slove(){
cin>>n>>k;
map<vector<int>,int>mp;
vector<vector<int> >a(n+1,vector<int>(k+1));
vector<int>v(k+1);
fel(i,1,n){
fel(j,1,k){
cin>>a[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
for(int w=1;w<=k;w++){
v[w]=(6-a[i][w]-a[j][w])%3;
}
mp[v]++;
}
}
int ans=0;
for(int i=1;i<=n;i++){
int x=mp[a[i]];
ans+=(x*(x-1)/2);
}
cout<<ans<<endl;
}
6.Problem - 1704C - Codeforces
想到确实很简单,但是我觉得这不应该只有1200分吧?考虑两个保护房间之间的room不会被感染所以考虑先预处理出来两个房间之间的好房间,然后从大到小排序先把大的保护好。
void slove(){
cin>>n>>k;
fel(i,1,k) cin>>a[i];
sort(a+1,a+1+k);
fel(i,1,k-1) sum[i]=a[i+1]-a[i]-1;
sum[k]=n+a[1]-a[k]-1;
sort(sum+1,sum+1+k,greater<int>() );
int cnt=0,m=0;
for(int i=1;i<=k;i++){
if(sum[i]-2*m>0){
int t=sum[i]-2*m;
if(t==1) cnt++;
else cnt+=t-1;
}
m+=2;
}
cout<<n-cnt<<endl;
}
7.Problem - 1698D - Codeforces
记住写交互题的时候不要define endl “\n”.
二分是显而易见的,如何判断一个区间内是否存在那个没被swap的数呢?计算本应出现再区间内的数有多少个。考虑一个数如果与区间外的数交换。那贡献为0,如果与区间内的数进行交换。那贡献为2.很明显如果区间内出现次数为奇数哥很明显就再这个区间内。
void slove(){
int n;
cin>>n;
int l=1,r=n;
while(l<r){
int mid=l+r>>1;
cout<<"? "<<l<<" "<<mid<<endl;
fflush(stdout);
int cnt=0;
for(int i=l;i<=mid;i++){
int x;
cin>>x;
if(x>=l&&x<=mid) cnt++;
}
if(cnt%2) r=mid;
else l=mid+1;
}
cout<<"! "<<l<<endl;
}
8.Problem - 1698C - Codeforces
首先可以考虑枚举是肯定不可以的。要取发现一些性质。很明显正数和负数最多是两个。如果是三个,按最大的三个正数相加是不可能存在更大的正数。负数时一样的道理。再考虑0,如果是两个0必然是和要求的,随便再找一个数都是满足条件的。三个0也是一样的。所以多个0可以看作一个0.所以满足条件的最后最多就五个数。然后直接暴力即可。
void slove(){
int n;
cin>>n;
vector<int>a;
int zero=0,pos=0,neg=0;
fel(i,1,n){
int x;
cin>>x;
if(x>0){
a.pb(x);
pos++;
}
if(x<0){
a.pb(x);
neg++;
}
if(x==0&&zero==0){
a.pb(x);
zero++;
}
}
if(pos>=3||neg>=3){
cout<<"NO"<<endl;
return;
}
sort(a.begin(),a.end());
int len=a.size();
for(int i=0;i<len;i++){
for(int j=i+1;j<len;j++){
for(int k=j+1;k<len;k++){
int t=lower_bound(a.begin(),a.end(),a[i]+a[j]+a[k])-a.begin();
if(a[t]!=a[i]+a[j]+a[k]){
cout<<"NO"<<endl;
return;
}
}
}
}
cout<<"YES"<<endl;
}
9.Problem - 1697C - Codeforces
对操作分析一下发现只能把b和c往前移动,并且过程中都的是a,b。
考虑从左至右遍历。先把前面的排好,那如果此时s[i] != t[i]就只能往后取寻找字母了。所以只能是a,b或者b,c这两种不想等的情况。然后s[p]往后找到第一个不等于此p位置的字母s[j],如果s[j] = t[p] 就可交换,否则不可以。
void slove(){
cin>>n; cin>>s; cin>>t;
int p=0;
while(p<n){
if(s[p]==t[p]) p++;
else if(t[p]-s[p]!=1){
cout<<"NO"<<endl;
return;
}
else{
int j=p;
while(s[j]==s[p]&&j<n) j++;
if(j==n||s[j]!=t[p]){
cout<<"NO"<<endl;
return;
}
swap(s[j],s[p]);
p++;
}
}
cout<<"YES"<<endl;
}
10.Problem - 1696C - Codeforces
很明显split和merge是互逆的操作。
一开始是用map统计把a数组全部split,然后再b数据全部split。统计最后map每一项是不是都是0。但是一交就wa2了。然后发现一个数据4,1和1,4这两个是不一样的。所以必须的保证顺序也是一致的。所以考虑用vector存全部切割之后的数组是否完全相等即可。
cin>>n>>m;标签:cnt,cout,int,fel,练习,基础,cin,Codeforces From: https://www.cnblogs.com/silky----player/p/16774186.html
vector<pair<int,int> >a,b;
fel(i,1,n){
int x,cnt=1;
cin>>x;
while(x%m==0){
x/=m;
cnt*=m;
}
if(a.empty()||a.back().first!=x){
a.pb({x,cnt});
}
else a.back().second+=cnt;
}
cin>>k;
fel(i,1,k){
int x,cnt=1;
cin>>x;
while(x%m==0){
x/=m;
cnt*=m;
}
if(b.empty()||b.back().first!=x){
b.pb({x,cnt});
}
else b.back().second+=cnt;
}
if(a==b) cout<<"YES"<<endl;
else cout<<"NO"<<endl;