2024牛客寒假算法基础集训营6

目录

A.宇宙的终结

B.爱恨的纠葛

C.心绪的解剖

D.友谊的套路

E.未来的预言

F.命运的抉择

G.人生的起落

I.时空的交织

J.绝妙的平衡

K.错综的统一


A.宇宙的终结

直接暴力

我们可以发现数据范围特别小题目特别简单,如果能够马上想到一个容易写的做法就可以马上写上去

bool check(int x){
    if(x==1) return false;
    for(int i=2;i<=x/i;i++)
        if(x%i==0) return false;
    return true;
}
void solve(){
    int l,r; cin>>l>>r;
    vector<int> v;
    for(int i=2;i<=r;i++){
        if(check(i)){
            v.push_back(i);
        }
    }
    for(int i=0;i<v.size();i++)
        for(int j=i+1;j<v.size();j++)
            for(int k=j+1;k<v.size();k++){
                int x=v[i]*v[j]*v[k];
                if(x>=l && x<=r){
                    cout<<x<<endl;
                    return ;
                }
            }
    cout<<-1<<endl;
    return ;
}

B.爱恨的纠葛

左右二分

明显的要求我们安排数的位置我们可以发现对于一个数在他的左右是具有二分性质的所有考虑两边同时使用二分即可

int a[N],b[N],op[N],ans[N];
bool st[N];
 
void solve(){
    
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) cin>>b[i];
    for(int i=1;i<=n;i++) ans[i]=2e9+10;
    sort(a+1,a+1+n);
     
    for(int i=1;i<=n;i++){
         
        int l=1,r=n;
        while(l<r){
            int mid=l+r>>1;
            if(a[mid]>=b[i]) r=mid;
            else l=mid+1;
        }
        if(a[l]>=b[i]){
            ans[i]=a[l]-b[i];
            op[i]=l;
        }
         
        l=1,r=n;
        while(l<r){
            int mid=l+r+1>>1;
            if(a[mid]<=b[i]) l=mid;
            else r=mid-1;
        }
        if(a[l]<=b[i]){
            if(b[i]-a[l]<ans[i]){
                ans[i]=b[i]-a[l];
                op[i]=l;
            }
        }
    }
    int res=2e9+10;
    for(int i=1;i<=n;i++){
        if(ans[i]<res) res=ans[i];
    }
    vector<int> now(n+5);
     
    for(int i=1;i<=n;i++){
        if(ans[i]==res){
            now[i]=op[i];
            st[op[i]]=true;
            break;
        }
    }
    int l=1;
    for(int i=1;i<=n;i++){
        if(!now[i]){
            while(st[l]) l++;
            now[i]=l;
            st[l]=true;
        }
        cout<<a[now[i]]<<' ';
    }
    cout<<endl;
    return ;
}

C.心绪的解剖

斐波那契的性质

我们可以知道斐波那契只要到了70多位之后面的数就会特别大,所以只需要考虑对70的数据范围做n^3即可

int dp[N];
map<int,TUP> mp;
void intn(){
    dp[0]=0,dp[1]=dp[2]=1;
    for(int i=2;i<=70;i++){
       dp[i]=dp[i-1]+dp[i-2];
    }
    for(int i=0;i<=70;i++)
        for(int j=0;j<=70;j++)
            for(int k=0;k<=70;k++)
                mp[dp[i]+dp[j]+dp[k]]={dp[i],dp[j],dp[k]};
}
void solve(){
    int x; cin>>x;
    if(mp.count(x)){
        auto [a,b,c]=mp[x];
        cout<<a<<' '<<b<<' '<<c<<endl;
    }
    else cout<<-1<<endl;
    return ;
}

D.友谊的套路

签到题

直接按照题目模拟即可

void solve(){
    
    double p,res=0;
    cin>>p;
    res=p*p*(1-p)*(1-p)*(1-p)+(1-p)*(1-p)*p*p*p;
    cout<<LF(7)<<res<<endl;
    return ;
}

E.未来的预言

我们可以发现这也是简单的模拟不过有一个小问题就是字符串读入数字的转化算一个小坑

void solve(){
    
    string s; cin>>s;
    string res; cin>>res;
    n=res.size(); res=' '+res;
    int x=0;
    for(int i=2;i<s.size();i++){
        x=x*10+(s[i]-'0');
    }
    x=x/2+1;
    int R=0,P=0;
    for(int i=1;i<=n;i++){
        if(res[i]=='R') R++;
        else P++;
        if(R==x){
            cout<<"kou!"<<endl;
            cout<<i<<endl;
            return ;
        }
        if(P==x){
            cout<<"yukari!"<<endl;
            cout<<i<<endl;
            return ;
        }
    }
    cout<<"to be continued."<<endl;
    cout<<n<<endl;
    return ;
}

F.命运的抉择

我们来找一下性质,也就是说如果b,c中任意取一个数出来都是gcd等于1说明如果两个数有其他公约数的话一定都是在一个数组里面比如 2,4一定是在一起的,也就是手说他们之间是有一条边的所以我们可以考虑把所有数的约数筛选出来,然后对他们的倍数进行连边,最后利用最小生成树的思路来合并,如果说都合并在一起那就是不可,否则可行

int a[N],p[N];
int find(int x){
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}    
 
void solve(){ 
 
     cin>>n;
     unordered_map<int,int> yes;
     for(int i=1;i<=n;i++){
        cin>>a[i];
        int x=a[i];
        
         
        if(!yes.count(x))p[x]=x,yes[x]++;
         
        for(int j=2;j<=x/j;j++){
            if(x%j==0){
                while(x%j==0) x/=j;
                if(!yes.count(j))p[j]=j,yes[j]++;
                int fx=find(a[i]),fj=find(j);
                if(fx!=fj) p[fx]=fj;
            }
        }
        if(x>1){
             
            if(!yes.count(x))p[x]=x,yes[x]++;
            int fx=find(x),fa=find(a[i]);
             
            if(fx!=fa) p[fx]=fa;
             
        }
         
    }
    unordered_map<int,int> use;
    for(int i=1;i<=n;i++){
        int x=a[i];
        if(find(x)==x) use[x]++;
        for(int j=2;j<=x/j;j++){
            if(x%j==0){
                while(x%j==0) x/=j;
                if(find(j)==j) use[j]++;
            }
        }  
        if(x>1 && find(x)==x) use[x]++;
    }
    int ans=use.size();
    if(ans==1){
        cout<<-1<<' '<<-1<<endl;
        return ;
    }
    int x=a[1];
    vector<int> b,c;
    for(int i=1;i<=n;i++){
        int fx=find(x),fa=find(a[i]);
        if(fx==fa)b.push_back(a[i]);
        else c.push_back(a[i]);
    }
    cout<<b.size()<<' '<<c.size()<<endl;
    for(auto&v:b) cout<<v<<' ';
    cout<<endl;
    for(auto&v:c) cout<<v<<' ';
    cout<<endl;
              
    return ;
}

G.人生的起落

细节构造

对于构造题我们需要一点一点的额来处理这个问题

1.首先构造一个v三元组至少需要三个

2.其次需要构造k个v三元组需要使用至少2k+1个位置

3.我们可以构造的最少消耗的v三元组是2 1 2

4.注意三元组的要求是两边都要相等

我们不妨开始判断

1.如果位置少不行

2.如果说s小于最少使用也不行

3.如果构造之后不是3元组不行

由此可以进行进一步的推理和演算

void solve(){
    
    cin>>n>>s>>k;
    
     
    int need = k ? (2*k+1) : 0;
     
    if(need>n) return cout<<-1<<endl,void();
     
    if(need==n && s>3*k+2){
         int res = 3*k+2;
         s-=res;
         if(s<k+1) return cout<<-1<<endl,void();
         vector<int> ans(n+5);
         for(int i=1;i<=n;i++){
            if(i&1) ans[i]=2;
            else ans[i]=1;
         }
         int t=s/(k+1),more=s%(k+1);
         for(int i=1;i<=n;i++){
            if(ans[i]==2) ans[i]+=t;
            else{
                ans[i]+=(more ? 1 : 0);
                if(more>0) more--;
            }
         }
         for(int i=1;i<=n;i++) cout<<ans[i]<<' ';
         cout<<endl;
         return ;
    }
    if(k){
       int res = 3*k+2 + (n-need);
       if(res>s) return cout<<-1<<endl,void();
       for(int i=1;i<=k;i++) cout<<2<<' '<<1<<' ';
       cout<<2<<' ';
       s-=3*k+2;
       n-=need;
       if(!n) return cout<<endl,void();
    }
    int x=s/n,more=s%n;
    vector<int> res;
    for(int i=1;i<=n;i++){
        cout<<(x + (more ? 1 : 0))<<' ';
        if(more>0) more--;
    }
    cout<<endl;
 
    return ;
}

I.时空的交织

对于这种题目我们都是去找性质可以发现其实两个数相乘就是两个区间中取出来一段做乘法,要求最后的结果最大,也就是{最大*最大,最大*最小,最小*最小,最小*最小}题目转化为区间中找一段连续的最大值或者连续的最小值,简单处理即可

int a[N],b[N];
int get1(int a[],int n){
    int suma=0,ansa=-1e9-10;
    for(int i=1;i<=n;i++){
        suma+=a[i];
        ansa=max(ansa,suma);
        if(suma<0) suma=0;
    }
    return ansa;
}
int get2(int a[],int n){
    int suma=0,ansa=1e9+10;
    for(int i=1;i<=n;i++){
        suma+=a[i];
        ansa=min(ansa,suma);
        if(suma>0) suma=0;
    }
    return ansa;
}
void solve(){
    
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=m;i++) cin>>b[i];
    LL res=max({get1(a,n)*get1(b,m),get1(a,n)*get2(b,m),get2(a,n)*get1(b,m),get2(a,n)*get2(b,m)});
    cout<<res<<endl;
    return ;

J.绝妙的平衡

树的性质

可以发现如果每一个红色的都至少有一个黑色直系的子节点就一定有解,但是最后的方案如何呢,可以发现只需要微调一下,不妨令所有的节点都是2,如果黑色节点之和不是3的倍数改变红节点为补数即可,否则把红节点改为1,找一个子儿子也改为1即可

vector<int> g[N];
int cnt[N],ans[N];
string s; 
vector<int> res;
void dfs(int u,int fa){
    for(auto&v:g[u]){
        if(v==fa) continue;
        dfs(v,u);
        if(s[v]=='W'){
            cnt[u]+=cnt[v];
        }
    }
    if(s[u]=='R'){
        if(cnt[u]==0){
            cout<<-1<<endl;
            exit(0);
        }
        int x=cnt[u]*2;
        if(x%3){
            ans[u]=3-(x%3);
        }
        else{
            ans[u]=1;
            for(auto&v:g[u]){
                if(v==fa) continue;
                if(s[v]=='W'){
                    ans[v]=1; break;
                }
            }
        }
        cnt[u]=0;
    }
    else{
        cnt[u]++;
    }
    
}
 
void solve(){
    cin>>n;
    cin>>s; s=' '+s;
    for(int i=2;i<=n;i++){
        int x; cin>>x;
        g[x].push_back(i);
        g[i].push_back(x);
    }
    for(int i=1;i<=n;i++) ans[i]=2;
    dfs(1,-1);
    for(int i=1;i<=n;i++) cout<<ans[i];
    cout<<endl;
    return ;
}

K.错综的统一

题目意思明显的就是不能有大于等于2的回文串,所以就是考虑只有三个字母也就是red,的排列,同时考虑竖着的也就是12种情况,我们可以先构造出来然后按照二维前缀和的方式来维护求和即可

构造可以先构造第一行然后第二行对应变化,对于2*2的格子需要特判

char s[M][M];
char g[M][M][13];
int ans[M][M][13];
string op[]={" ","red","red","rde","rde","edr","edr","erd","erd","der","der","dre","dre"};
string A[]={" ","re","er","rd","dr","ed","de"};

void solve(){
    
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++) 
        for(int j=1;j<=m;j++)
            cin>>s[i][j];
    
    for(int c=1;c<=12;c++){
        for(int j=1,k=0;j<=n;j++,k++){
            k%=3;
            g[1][j][c]=g[1][j][c+1]=op[c][k];
        }
        g[2][1][c]=(c&1 ? g[1][2][c] : g[1][3][c]);
        g[2][2][c]=(c&1 ? g[1][3][c] : g[1][1][c]);
        for(int i=2;i<=n;i++)
            for(int j=1;j<=m;j++)
                    if(!g[i][j][c]){
                        for(auto&t:{'r','e','d'}){
                            if(t!=g[i-1][j][c] && t!=g[i-2][j][c] && t!=g[i][j-1][c] && t!=g[i][j-2][c]){
                                g[i][j][c]=t; break;
                            }
                        }
                    }
         
    }
    for(int u=1;u<=12;u++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                ans[i][j][u] = s[i][j]!=g[i][j][u];
                ans[i][j][u]+=ans[i-1][j][u];
                ans[i][j][u]+=ans[i][j-1][u];
                ans[i][j][u]-=ans[i-1][j-1][u];
            }
        }
    }
    
    while(q--){
        int x1,y1,x2,y2; cin>>x1>>y1>>x2>>y2;
        int res=n*m;
        if(x2-x1==1 && y2-y1==1){
            for(int i=1;i<=6;i++){
                res=min(res,(s[x1][y1]!=A[i][0]) + (s[x1][y1+1]!=A[i][1]) + (s[x1+1][y1]!=A[i][1]) + (s[x1+1][y1+1]!=A[i][0]) );
            }
        }
        for(int i=1;i<=12;i++){
            res=min(res,ans[x2][y2][i]-ans[x2][y1-1][i]-ans[x1-1][y2][i]+ans[x1-1][y1-1][i]);
        }
        cout<<res<<endl;
    }
    return ;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值