Codeforces Round #460 (Div. 2)A~E(F待补)个人题解

A. Supermarket

题意:小明现在要买m个苹果,有n个商店,苹果在第i个商店是a_i元买b_i个,现在问你小明最少花费多少钱?

知识点:贪心

思路:我们肯定是性价比最高的一直买,这样能买最多。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N= 5e3 + 5;
const int mod = 998244353;
void solve(){
    int n,m;cin>>n>>m;
    double ans=1e9;
    for(int i=1;i<=n;++i){
        int x,y;
        cin>>x>>y;
        ans=min(ans,x*1.0/y);//一个苹果最少多少钱能买到
    }
    cout<<fixed<<setprecision(8)<<m*ans<<'\n';
    return ;
}
signed main(){
    ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);
    int _ = 1 ;//cin >> _ ;
    while( _-- ){
        solve() ;
    }
    return 0;
}

B. Perfect Number

题意:我们定义一个好数是他的所有位数上的数加起来等于10,现在问你第k个好数是谁?

知识点:暴力

思路:我们可以从1~2e7暴力,是好数就个数++,然后到第k个就输出就好了。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N= 5e3 + 5;
const int mod = 998244353;
void solve(){
    int n;cin>>n;
    int ans=0;
    for(int i=1;i<=2e7;i++){
        int x=i,sum=0;
        while(x){
            sum+=x%10;//每一位上的数字求和
            x/=10;
        }
        ans+=(sum==10);//如果为10就++
        if(ans==n){
            cout<<i<<'\n';//到第k个了,输出答案
            return ;
        }
    }
    return ;
}
signed main(){
    ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);
    int _ = 1 ;//cin >> _ ;
    while( _-- ){
        solve() ;
    }
    return 0;
}

C. Seat Arrangements

题意:有一个n \times m大小的矩阵,矩阵中只有'.'和‘*’,现在问你能在所有行和所有列中找到多少个连续的长度为k的连续的‘.’?

知识点:数学

思路:

如果k>1,我们在每一行找有多少个连续的‘.’,如果这个长度l是大于等于k的,答案就加上l-k+1.

然后再每一列找有多少个连续的‘.’,如果这个长度l是大于等于k的,答案就加上l-k+1.

如果k=1,可以发现我们上面的思路会让答案比真正的答案多一倍,因为我们行找后,列又找了一遍,所以答案除以2就可以了。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N= 5e3 + 5;
const int mod = 998244353;
char mp[2005][2005];
ll ans=0;
void solve(){
    int n,m,k;cin>>n>>m>>k;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            cin>>mp[i][j];//输入矩阵
        }
    }
    for(int i=1;i<=n;++i){//计算每一行
        int cnt=1;
        char pre=mp[i][1];
        for(int j=2;j<=m;++j){
            if(mp[i][j]==pre)cnt++;
            else {
                if(cnt-k+1>0&&pre=='.')ans+=cnt-k+1;//如果cnt>=k并且是'.'的连续
                pre=mp[i][j];
                cnt=1;
            }
        }
        if(cnt-k+1>0&&pre=='.')ans+=cnt-k+1;
    }
    for(int i=1;i<=m;++i){//计算每一列
        int cnt=1;
        char pre=mp[1][i];
        for(int j=2;j<=n;++j){
            if(mp[j][i]==pre)cnt++;
            else {
                if(cnt-k+1>0&&pre=='.')ans+=cnt-k+1;//如果cnt>=k并且是'.'的连续
                pre=mp[j][i];
                cnt=1;
            }
        }
        if(cnt-k+1>0&&pre=='.')ans+=cnt-k+1;
    }
    if(k==1)ans>>=1;//如果k==1的特判
    cout<<ans<<'\n';
    return ;
}
signed main(){
    ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);
    int _ = 1 ;//cin >> _ ;
    while( _-- ){
        solve() ;
    }
    return 0;
}

D. Substring

题意:有一个n个点,m条边的有向图,图上每一个点都有一个小写字符,现在让你找到一条路线,让路线上某一个字符出现的次数最多,如果次数无限多就输出-1。

知识点:拓扑排序,DP

思路:可以发现,如果图中有环的话,那我们一直在环上绕圈圈,就能满足次数无限多,

所以只要有环就输出-1,现在问题就是一个有向无环图,肯定先想DP啊,发现确实可以,

定义dp[i][26],表示到当前位置每一个字符出现的次数

转移就是 \sum_{i=0}^{26}dp[v][i]=max(dp[v][i],dp[u][i]+(c[i]==i))

u可以走到v,c[i]是这个点的字符是谁

所以代码也就出来了,拓扑排序一下,DP转移,最后判一下每个点的入度是不是0(就是找环)

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N= 3e5 + 5;
const int mod = 998244353;
int dp[N][26],in[N];
vector<int>mp[N];
int c[N];
void solve(){
    int n,m;cin>>n>>m;
    for(int i=1;i<=n;++i){
        char ch;cin>>ch;
        c[i]=ch-'a';
    }
    for(int i=1;i<=m;++i){
        int u,v;cin>>u>>v;
        mp[u].push_back(v);
        in[v]++;//计算入度
    }
    queue<int>q;
    int ans=0;
    for(int i=1;i<=n;++i){
        if(!in[i])q.push(i),ans=1,dp[i][c[i]]=1;//入度为0的入队,dp初始化
    }
    while(!q.empty()){
        int u=q.front();q.pop();
        for(auto v:mp[u]){
            for(int i=0;i<26;++i){
                dp[v][i]=max(dp[v][i],dp[u][i]+(c[v]==i));//转移
                ans=max(ans,dp[v][i]);//找答案
            }
            in[v]--;//入度--
            if(!in[v])q.push(v);//入度为0,就入队
        }
    }
    for(int i=1;i<=n;++i){
        if(in[i]){//如果有入度不是0的点,说明有环
            cout<<-1<<'\n';
            return ;
        }
    }
    cout<<ans<<'\n';
    return ;
}
signed main(){
    ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);
    int _ = 1 ;//cin >> _ ;
    while( _-- ){
        solve() ;
    }
    return 0;
}

E. Congruence Equation

题意:n\times a^n \equiv b(mod \ p),n \in [1,x],问你有多少个n成立?

知识点:数论(求逆元,循环节,费马小定理),滑动思想

思路:更具取模的定义我们知道n在[0,p)循环,再根据费马小定理我们知道a^n[a^0.........a^{p-1})循环,他们一个长度是p,一个长度是p-1,他们一定一奇一偶,所以一定互质,所以总的循环节就是p\times (p-1),然后对于每一个a^i他一定能在[0,p-1)中找到一个答案满足条件,所以一个循环节的答案个数就是p-1,现在问题就变成了一个循环节缺失一点,找其中多少个a^i还满足条件,我们通过一些手动的模拟,可以发现一个规律,如下

 发现对于一个a^i,他后面先开始找到一定是i,然后单调减到0,然后再p-1开始单调减

如果丢失,我们怎么找答案?

 如图,我们现在只有红色部分,他们每一个的查找区间是什么呢,其实就是这个部分对p-1先除,确定最小的几个长度,然后看余数是几,再把上面的区间长度+1,我们要找什么?

问题就是x \equiv b*inv(a^i)(mod\ p),在区间里找是不是存在,我们发现这个区间不是很好表示,

我们可以二倍这个原区间这样就是循环的了,如下

就这样,然后我们现在问题变成了怎么区间查询一个数x是否存在?

发现区间是连续的,所以暴力找第一个区间后,加首,减尾,依次移动就可以了。

这样这个题就解决了。

代码

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N= 2e6 + 15;
const int mod = 998244353;
ll ksm(ll a,ll b,ll p){
    ll ans=1;
    while(b){
        if(b&1)ans=ans*a%p;
        a=a*a%p;
        b>>=1;
    }
    return ans;
}
int pp[N],l=-1,r=-1;//注意初始化,因为我们区间是从0开始的
int vis[N];
int Find(int beg,int len,int x){//跟莫队差不多
    while(r>beg+len-1){
        vis[pp[r]]--;
        r--;
    }
    while(r<beg+len-1){
        r++;
        vis[pp[r]]++;
    }
    while(l<beg){
        if(l!=-1)vis[pp[l]]--;
        l++;
    }
    while(l>beg){
        l--;
        vis[pp[l]]++;
    }
    return vis[x]>0;
}
void solve(){
    ll a,b,p,x;cin>>a>>b>>p>>x;
    x++;//这里x++,是因为题目是从1开始的,我是从0开始的,但是b>1所以0*a^0!=b,所以我这样
    for(int i=0;i<p;++i){
        pp[i]=p-i-1;
        pp[i+p]=pp[i];//二倍简化循环
    }
    ll d=x/(p*(p-1)),now=x%(p*(p-1));//大循环周期
    ll len=now/(p-1),ru=now%(p-1);//不是整个周期,找我们的那个规律
    ll ans=d*(p-1);//大循环周期个数乘(p-1)个答案
    for(int i=0;i<p-1;++i){
        int l=len+(ru>i);//因为我x++了,所以这里没等号
        ll x=b*ksm(ksm(a,i,p),p-2,p)%p;//确定x
        ll o=Find(p-i-1,l,x);//找x,存在答案就+1
        ans+=o;
    }
    cout<<ans<<'\n';//输出答案
}
signed main(){
    ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);
    int _ = 1 ;//cin >> _ ;
    while( _-- ){
        solve() ;
    }
    return 0;
}

F. A Game With Numbers

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值