Harbour.Space Scholarship Contest 2021-2022 (Div. 1 + Div. 2) -29

自闭场,d题fst了,掉大分^^

A 题意

如果数字x的各个位数加和比x+1的大,称为一个特殊数字,问给出n,小于等于n的特殊数字个数

A 思路

这能wa1,我真服了。。显然只有9,19,29,等等尾巴是9的可以,那么答案就是(n+1)/10。代码实现和我嘴的思路不一样,不过签到题懒得改了。

A 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=400505;
	const int inf=0x3f3f3f3f;
	int n,m,k;
	void YES(){
		cout<<"YES"<<endl;
	}
	void NO(){
		cout<<"NO"<<endl;
	}
	void solve(){
        string s;
        cin>>s;
        if(s.size()==1){
            if(s=="9")  cout<<1<<endl;
            else    cout<<0<<endl;
        }
        else{
            int ans=0;
            for(int i=0;i<s.size()-1;i++){
                ans*=10;
                ans+=s[i]-'0';
                
            }
            if(s[s.size()-1]=='9')  ans++;
            cout<<ans<<endl;
        }
	}
	signed main(){
        IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
		cin>>tn;
		while(tn--){
			solve();
		}
	} 
	
						
B 题意

给你串a和b,你开始时选一个a的字符,只有向右移动若干次,再向左移动若干次,把所有接触到的字符都记录下来,问是否可以获得b

B 思路

长度小于500,暴力枚举就行了

B 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=400505;
	const int inf=0x3f3f3f3f;
	int n,m,k;
	void YES(){
		cout<<"YES"<<endl;
	}
	void NO(){
		cout<<"NO"<<endl;
	}
	void solve(){
        string a,b;
        cin>>a>>b;
        n=a.size(),m=b.size();
        a='z'+a;
        for(int i=1;i<=n;i++){
            for(int len=0;len<=m;len++){
                string t;
                t+=a[i];
                for(int j=i+1;j-i<=len;j++){
                    t+=a[j];
                }
                for(int j=i+len-1,tmp=0;tmp<m-1-len&&j;j--,tmp++){
                    t+=a[j];
                }
                if(t==b){
                    YES();
                    return ;
                }
            }
        }
        NO();
        return ;
	}
	signed main(){
        IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
		cin>>tn;
		while(tn--){
			solve();
		}
	} 
	
						
C 题意

两队挨个踢球,1表示进了0没进?都有机会。如果某次踢完球后胜负已分(比如a全进b全不进a都要输),就终止,一共十轮,问最早可能结束的轮次。

C 思路

2^10=1024,直接dfs

C 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=400505;
	const int inf=0x3f3f3f3f;
	int n,m,k;
	void YES(){
		cout<<"YES"<<endl;
	}
	void NO(){
		cout<<"NO"<<endl;
	}
    string s;
    int ans;
    map<int,int>last;
    void dfs(int x,int a,int b){
        if(x>10)    return ;
        int lasta=last[x],lastb=10-x+1-lasta;
        if(lasta+a<b||lastb+b<a){
            ans=min(ans,x-1);
            return ;
        }
        int who=x%2;//1 先手
        if(s[x]=='1'){
            if(who)
                dfs(x+1,a+1,b);
            else
                dfs(x+1,a,b+1);
        }
        else if(s[x]=='0'){
            dfs(x+1,a,b);
        }
        else{
            if(who)
                dfs(x+1,a+1,b);
            else
                dfs(x+1,a,b+1);
            dfs(x+1,a,b);
        }
    }
	void solve(){
        cin>>s;
        s='z'+s;
        ans=10;
        dfs(1,0,0);
        cout<<ans<<endl;
	}
	signed main(){
        IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
		cin>>tn;
        last[1]=5;
        last[2]=4;
        last[3]=4;
        last[4]=3;
        last[5]=3;
        last[6]=2;
        last[7]=2;
        last[8]=1;
        last[9]=1;
        last[10]=0;
		while(tn--){
			solve();
		}
	} 
	
						
D 题意

给你一个串,遍历他,你可以做以下操作

  1. 写下这个字符
  2. 不写这个字符,并且删除上一个
    是否能得到另一个串?
D 思路

想复杂了。我的思路是这样一来,相当于让新串每一个字符找到一个匹配位置,满足这个对应的位置单增,且匹配位置下标相减为奇数。于是按奇偶性把原串拆了,在两个新串里搞双指针,成功fst了。原因是没考虑最后匹配掉后必须删完。再加条件时发现过于麻烦了,遂放弃。

学习脱老师解法,显然知道我们最后删完的话,最后一个匹配位置后面字符应当为偶数个,前面则没有限制。于是我们reverse字符串,从后向前搞,如果匹配到了,就去找下一个,否则下标加2,因为需要删掉这个没匹配字符。

D 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=400505;
	const int inf=0x3f3f3f3f;
	int n,m,k;
	void YES(){
		cout<<"YES"<<endl;
	}
	void NO(){
		cout<<"NO"<<endl;
	}
	void solve(){
        string a,b,t;
        cin>>a>>b;
        n=a.size(),m=b.size();
        if(n<m){
            NO();
            return ;
        }
        reverse(a.begin(),a.end());
        reverse(b.begin(),b.end());
        int i=0,j=0;
        for(;j<m;j++){
            while(i<n&&a[i]!=b[j])i+=2;
            if(i>=n){
                NO();
                return ;
            }
            i++;
        }
        YES();
        return ;
	}
	signed main(){
        IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
		cin>>tn;
		while(tn--){
			solve();
		}
	} 
	
						
E 题意

一个排列1234567…n,我们定义k轮换为把最后k个放到前面来,也就是n-k+1,n-k+2…n,1,2,3,4,5…n-k。然后我们至多了两两交换m次,给出你操作后数组,问有几个k可能导致这种情况。

E 思路

前置知识,我们考虑012345…n-1这种排列

  • 我们能发现,k轮换后的元素ai=(i-k)mod k,我们可以根据现在的ai倒推出k。当然,没发现前面那个mod关系也无所谓,也是可以倒退的。
  • 一个well known的知识,交换多少次数字可以使元素有序,如果是相邻,是逆序对数,如果是任意,那么i向离散化的ai连边,答案是n-联通块数。

首先,我们给出一个k,可以利用第二点测算他能否由小于等于m次交换操作形成最终结果,复杂度O(n)。题目中的m最大为n/3,假设我们最优化m操作,也只能使2n/3个数字和原来的位置不一样,那么也就是说,我们可以预处理每一个数字假如在轮换后就没动过,他的轮换次数k是几。我们记录一下每个k出现了几次,只有某个k出现次数大于n/3的情况下,他才有机会成为答案,显然这样的k最多有三个,我们对这样的k单独判断一下即可。复杂度O(n)

E 代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib> 
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=400505;
	const int inf=0x3f3f3f3f;
	int n,m,k;
    int a[maxn];
	int b[maxn];
    void YES(){
		cout<<"YES"<<endl;
	}
	void NO(){
		cout<<"NO"<<endl;
	}
    map<int,int>mp;
    vector<int>ans;
    int fa[maxn];
    int find(int x){
        return x==fa[x]?fa[x]:fa[x]=find(fa[x]);
    }
    bool judge(int x){
        int cnt=n;
        for(int i=1;i<=n;i++){
            int t=(i-1-x+n)%n+1;
         //   cout<<t<<' ';
            if(a[i]!=t){
                if(find(a[i])!=find(t)){
                    fa[find(a[i])]=find(t);
                    cnt--;
                }
            }
        }
        //cout<<endl;
        return n-cnt<=m;
    }
	void solve(){
        mp.clear();
        cin>>n>>m;
        ans.clear();
        for(int i=1;i<=n;i++){
            cin>>a[i];
            b[i]=(i-a[i]+n)%n;
            mp[b[i]]++;
        }
        for(auto p:mp){
            if(p.second+2*m>=n){
             //   cout<<p.first<<' '<<p.second<<endl;
                for(int i=0;i<=n;i++) fa[i]=i;
                if(judge(p.first))
                    ans.push_back(p.first);
            }
        }
        cout<<ans.size();
        for(auto i:ans) cout<<' '<<i;
        cout<<endl;
	}
	signed main(){
        IOS
		#ifndef ONLINE_JUDGE
		    freopen("IO\\in.txt","r",stdin);
		    freopen("IO\\out.txt","w",stdout);
        #endif
		int tn=1;
		cin>>tn;
		while(tn--){
			solve();
		}
	} 
	
						
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值