菜菜多校1 题解

H - Local Minimum(签到)

https://codeforces.com/gym/103447/problem/J

题意:

给一个矩阵,问 有多少个元素满足:既是当前行的最小值,又是当前列的最小值

思路:

用两个数组维护每一行每一列最小值,n*m 遍历判断即可

样例:

3 3
1 5 9
4 3 7
2 6 2

代码:

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=1e3+5;
int a[N][N];
int mn1[N],mn2[N];

void solve(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)mn1[i]=INT_MAX;
    for(int i=1;i<=m;i++)mn2[i]=INT_MAX;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>a[i][j];
            mn1[i]=min(mn1[i],a[i][j]);
            mn2[j]=min(mn2[j],a[i][j]);
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(a[i][j]==mn1[i]&&a[i][j]==mn2[j])ans++;
        }
    }
    cout<<ans<<endl;
    return;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
    while(T--){
        solve();
    }
    return 0;
}

E - Shinyruo and KFC

https://codeforces.com/gym/103428/problem/G

题意:

有 n 种食品,每种食品有 a [ i ] 个,有 k 个人,每个人每种食品 最多拿一个,可以拿多种,求把所有食品分配完的方案数,当 k 取 1到m 时,分别输出答案

思路:

问题可以转化为,n项 C ( k , a [ i ] ) 的乘积

发现对于某个 a[i] ,对答案的贡献是一定的,开个map,记录下 每个数量对应的食品个数,通过快速幂求解

代码:

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;

#define int long long

const int N=1e5+5;
int mod=998244353;
int fact[N],inv[N];		//O(1)求组合数
int qpow(int a,int b){
    int ans=1;
    while(b>0){
        if(b&1)
        ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
}
void Cinit(){
    fact[0]=1,inv[0]=1;
    for(int i=1;i<=N-1;i++){
        fact[i]=fact[i-1]*i%mod;
        //inv[i]=inv[i-1]*qpow(i,mod-2)%mod;
    }
    inv[N-1]=qpow(fact[N-1],mod-2)%mod;
    for(int i=N-2;i>=1;i--){
        inv[i]=inv[i+1]*(i+1)%mod;
    }
}
int C(int a,int b){
    if(a<0||b<0||a<b)return 0;
    return fact[a]*inv[a-b]%mod*inv[b]%mod;
}

int a[N];
void solve(){
    Cinit();
    int n,m;
    cin>>n>>m;
    int mx=0;
    map<int,int>mp;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(mp.find(a[i])==mp.end())mp[a[i]]=1;
        else mp[a[i]]++;
        mx=max(mx,a[i]);
    }
    sort(a+1,a+1+n);
    int tmp=min(m,mx-1);
    for(int i=1;i<=tmp;i++){
        cout<<0<<endl;
    }
    for(int i=tmp+1;i<=m;i++){
        int ans=1;
        for(auto j:mp){
            ans*=qpow(C(i,j.first),j.second);
            ans%=mod;
        }
        cout<<ans<<endl;
    }
    return;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    int T=1;
    //cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

F - Pudding Store

题意:

构造一个排列,使得 前 i 项的和*2 是 i 的倍数,求排列的个数

思路:

限制条件不好处理,直接打表看看有没规律,发现是3*2^(n-3)

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long

int a[105];
void solve(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)a[i]=i;
    int ans=0;
    do{
        int tmp=0;
        ans++;
        for(int i=1;i<=n;i++){
            tmp+=2*a[i];
            if(tmp%i!=0){
                ans--;
                break;
            }
        }
    }while(next_permutation(a+1,a+1+n));
    cout<<ans<<endl;
    return;
}

signed main(){

    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

I - Magical Subsequence

题意:

给一个长为 n 的序列,每一项不超过100,存在子序列满足 对所有k为奇数,第k项+第k+1项相等,求这个序列最长长度

思路:

枚举第k项和第k+1项的和,然后遍历序列,用 vis 记录扫过的可以取的值,queue将全部清空操作优化为 O(n),如果可以组成一个和,就清空queue

代码:

#include<bits/stdc++.h>
#define endl '\n'
#define debug(x) cout<<#x<<" = "<<x<<endl;
using namespace std;

#define int long long

const int N=1e5+5;
int a[N],vis[205];

void solve(){
    int n;
    cin>>n;
    int mx=1;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        mx=max(mx,a[i]);
    }
    int ans=0;
    for(int aim=2;aim<=2*mx;aim++){
        int len=0;
        queue<int>q;
        for(int i=1;i<=n;i++){
            if(a[i]>=aim)continue;
            int tmp=aim-a[i];
            if(vis[tmp]){
                len+=2;
                while(!q.empty()){
                    vis[q.front()]=0;
                    q.pop();
                }
            }
            else{
                if(vis[a[i]])continue;
                q.push(a[i]);
                vis[a[i]]=1;
            }
        }
        while(!q.empty()){
            vis[q.front()]=0;
            q.pop();
        }
        ans=max(ans,len);
    }
    cout<<ans<<endl;
    return;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    int T=1;
    //cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

A - Sergey and Subway 

https://www.luogu.com.cn/problem/CF1060E

题意:

给一颗树,给所有距离为2的点对 建一条新边,求新图上 所有两点间距离的和

思路:

考虑原图上两点u,v,在新图上 一次可以走原图的两步,如果dis(u,v) 是偶数,新距离为dis/2,如果dis(u,v)为奇数,新距离为(dis+1)/2

合并所有答案 ans=(原图上所有点对dis的和 + 原图上dis为奇数的点对数量)/2

前者我们通过枚举每条边的贡献,在一遍dfs中处理出来,后者我们可以将原树奇偶分层,距离为奇数的两点一定在奇偶不同的两层,计算出两层的点数,相乘即是点对的数量

代码:

 

#include<bits/stdc++.h>
using namespace std;

#define int long long

const int N=2e5+5;
int head[N],cntt=0;
struct Edge{
    int to,next;
}edge[2*N];
void add(int u,int v){
    edge[++cntt].to=v;
    edge[cntt].next=head[u];
    head[u]=cntt;
}
int n,siz[N],ans,cnt[2],flag[N];

void dfs(int x,int f){
    cnt[flag[x]]++;    //统计奇偶层的点数
    siz[x]=1;
    for(int i=head[x];i;i=edge[i].next){
        int y=edge[i].to;
        if(y==f)continue;
        flag[y]=flag[x]^1;
        dfs(y,x);
        siz[x]+=siz[y];
        ans+=(n-siz[y])*siz[y];    //计算每条边的贡献
    }
}

void solve(){
    cin>>n;
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        add(u,v);
        add(v,u);
    }

    dfs(1,1);

    ans+=cnt[0]*cnt[1];
    cout<<ans/2<<endl;
    return;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T=1;
    while(T--){
        solve();
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Auroraaaaaaaaaaaaa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值