周六训练赛2020江苏省省赛

🎈C - Cats

题意

有N个小猫(N小于1e5),每个猫的高度1~20,要求按身高猫排列,相同的身高的猫之间最矮的那只不得高于这两只猫,且相同的猫不能并排

思路

明显的构造题,2^20>1e5

可以看出身高为1的只能有1只,身高为2的只能在1的左右各一只,于是就想到如何构造,递归即可

AC代码

#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false)
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define ll long long
#define mod 1000000007
#define pb push_back
#define inf 0x3f3f3f3f
#define eps 0.00000001
const double pi = acos(-1);
using namespace std;
const int N = 2e5+10;
int a[N];
void dfs(int l,int r,int num){
    if(l>r)return;
    int mid=(l+r)>>1;
    a[mid]=num;
    if(l==r)return;
    dfs(l,mid-1,num+1);
    dfs(mid+1,r,num+1);
}
void solve(){
    int n;
    cin>>n;
    dfs(1,n,1);
    for(int i=1;i<=n;i++){
        cout<<a[i]<<" ";
    }cout<<endl;
}
int main(){
    IOS;
    solve();
    return 0;
}

🎈D - Delete Prime

题意

给你一个1~n(n<1e6)长度,值为下标的数组,给你重新构造数组,构造如下:

先把数组里的下标为质数 or 1的按顺序放好,然后再把剩下的未排的数从小到大排序,再按下标为质数 or 1,直到排完

举个1~10的例子

1 2 3 5 7

4 6 8 9 10(1 2 3 4 5)

排完序为1 2 3 5 7 4 6 8 10 9

然后现在有两个操作

操作1,输入数组长度n和k,k表示数组值为k的下标

操作2,输入数组长度n和k,k表示数组下标为k的值

思路

前缀和,1~n有多少质数,然后dfs(n-qian[n]),这样为了查找排序操作需要最多几次,最大是80次,单纯的我以为是1e6除去1e6里面的质数个数,这里也要直接写80,不然铁tle9

再前缀和一下,表示这个数是第几轮第几个出去的

把n处理一下,变成几轮几个结束的状态,然后进行查找

操作一很简单,只要前缀和加一下即可,操作二,要进行for循环查找第几轮,二分查找第几个出去的

AC代码

#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false)
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define ll long long
#define mod 128
#define pb push_back
#define inf 0x3f3f3f3f
#define eps 0.00000001
const double pi = acos(-1);
using namespace std;
const int N = 1e6+10;
int qian[N],phi[N],p[N],cnt,vis[N];
int a[N][100],num[N],b[100];
void P(){
	cnt=0;
	for(int i=2;i<N;i++){
		if(!vis[i])p[++cnt]=i;
		for(int j=1;j<=cnt&&i*p[j]<N;j++){
			vis[i*p[j]]=1;
			if(i%p[j]==0){
				break;
			}
		}
	}
	for(int i=1;i<N;i++) qian[i]=qian[i-1]+(!vis[i]);
}
int dfs(int n){
	if(!vis[n]) return 1;
	return 1+dfs(n-qian[n]);
}
int er(int pos,int k){
	int l=1,r=N-1;
	while(l<r){
		int mid=(l+r)>>1;
		if(a[mid][pos]>=k) r=mid;
		else l=mid+1;
	}
	return l;
}
void solve(){
    int t;
    cin>>t;
    while(t--){
        int c,n,k;
        cin>>c>>n>>k;
        int now=n,len=0;
        while(now){
            b[++len]=qian[now];
            now-=qian[now];
        }
        if(c==1){
            int s=0;
            for(int i=1;i<num[k];i++) s+=b[i];
            cout<<(s+a[k-1][num[k]]+1)<<endl;
        }else{
            for(int i=1;i<=len;i++){
                if(b[i]>=k){
                    cout<<er(i,k)<<endl;break;
                }
                k-=b[i];
            }
        }
    }
}
int main(){
    IOS;
    P();
   // int mx=-1;
    for(int i=1;i<N;i++) num[i]=dfs(i),a[i][num[i]]=1;//mx=max(mx,num[i]);
    for(int i=1;i<=80;i++){
        for(int j=1;j<N;j++){
            a[j][i]+=a[j-1][i];
        }
    }
    solve();
    return 0;
}
/*
*/

🎈H - Happy Morse Code

题意

有长度为N(N<1e5)的01字符串,M(M<26)个长度为5以内的s和t字符串,其中t为01字符串

问M个t字符串能不能替代01字符串,能替代多少种,答案取余128

思路

因为取余128,会有一种情况就是1和129都有可能输出happymorsecode,0和128有可能输出nonono

所以要标记是否达到128以上

第一想法是kmp跑,然后感觉时间铁超时,于是想到了dp

用map存一下t字符串的个数

动态转移方程:dp[i]=dp[i]+dp[i-t.len]*mp[t];

然后就标记是否有超过128就行了

AC代码

#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false)
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define ll long long
#define mod 128
#define pb push_back
#define inf 0x3f3f3f3f
#define eps 0.00000001
const double pi = acos(-1);
using namespace std;
const int N = 1e6+10;
int dp[N];
int vis[N];
void solve(){
    int t;cin>>t;
    while(t--){
        mem(dp,0);
        mem(vis,0);
        int n,m;cin>>n>>m;
        map<string,int>mp;
        for(int i=0;i<m;i++){
            string a,b;
            cin>>a>>b;
            mp[b]++;
        }
        string s;
        cin>>s;
        dp[0]=1;
        int l=s.size();
        s=' '+s;
        for(int i=1;i<=l;i++){
            for(int j=1;j<=5&&j<=i;j++){
                string c=s.substr(i-j+1,j);//cout<<c<<" "<<(i-j+1)<<j<<endl;取第i-j+1后面j个字符串
                if(mp[c]){
                    if(vis[i-j])vis[i]=1;
                    if(dp[i-j]*mp[c]>=128 || dp[i]+dp[i-j]*mp[c]>=128) vis[i]=1;
                    dp[i]=dp[i]+dp[i-j]*mp[c]%mod;dp[i]%=mod;
                }            
            }
        }
        if(dp[l]==1 && !vis[l]){
            cout<<"happymorsecode"<<endl;
        }
        else if(dp[l] || (!dp[l] && vis[l])){
            cout<<"puppymousecat "<<dp[l]<<endl;
        }
        else{
            cout<<"nonono"<<endl;
        }
    }
}
int main(){
    IOS;
    solve();
    return 0;
}
/*
2
4 2
a 01
b 01
0101
5 1
a 1
11111
4
1
*/

🎈J - Just Multiplicative Inverse

题意

给一个递归程序F,问输入一个质素P,问F(X,P),X[1,P-1]平均要递归几次

F程序简化版

while(p%x>=1){
     x=p%x;
}

思路

他们都是暴力过的,我暴力tle???

然后我想了很久,看出了一个线性关系,然后就打了个内表,过了

AC代码

#include<bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false)
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define ll long long
#define mod 1000000007
#define pb push_back
#define inf 0x3f3f3f3f
#define eps 0.00000001
const double pi = acos(-1);
using namespace std;
const int N = 1e6+10;
int a[N];
inline void solve(){
    int n,t;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        double ans=0;
        for(int i=1;i<n;i++){
            if(n%i==0){
                a[i]=1;
            }
            else{
                a[i]=a[n%i]+1;
            }
        }
        for(int i=1;i<n;i++){
            ans+=a[i];
        }
        printf("%.8f\n",ans/(n-1));
    }
}
int main(){
    solve();
    return 0;
}

posted @ 2021-03-14 15:34  ouluy  阅读(91)  评论(0编辑  收藏  举报