A. Supermarket
题意:小明现在要买m个苹果,有n个商店,苹果在第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
题意:有一个大小的矩阵,矩阵中只有'.'和‘*’,现在问你能在所有行和所有列中找到多少个连续的长度为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],表示到当前位置每一个字符出现的次数
转移就是
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成立?
知识点:数论(求逆元,循环节,费马小定理),滑动思想
思路:更具取模的定义我们知道n在循环,再根据费马小定理我们知道在循环,他们一个长度是p,一个长度是p-1,他们一定一奇一偶,所以一定互质,所以总的循环节就是,然后对于每一个他一定能在中找到一个答案满足条件,所以一个循环节的答案个数就是p-1,现在问题就变成了一个循环节缺失一点,找其中多少个还满足条件,我们通过一些手动的模拟,可以发现一个规律,如下
发现对于一个,他后面先开始找到一定是i,然后单调减到0,然后再p-1开始单调减
如果丢失,我们怎么找答案?
如图,我们现在只有红色部分,他们每一个的查找区间是什么呢,其实就是这个部分对p-1先除,确定最小的几个长度,然后看余数是几,再把上面的区间长度+1,我们要找什么?
问题就是,在区间里找是不是存在,我们发现这个区间不是很好表示,
我们可以二倍这个原区间这样就是循环的了,如下
就这样,然后我们现在问题变成了怎么区间查询一个数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;
}