ABC近期题目合集

文章目录

    • AtCoder Beginner Contest 336 C
        • 问题陈述
        • 思路
    • AtCoder Beginner Contest 336 D
        • 问题陈述
        • 思路
    • AtCoder Beginner Contest 335 E
        • 问题陈述
        • 思路
    • AtCoder Beginner Contest 334 C
        • 问题陈述
        • 思路
    • AtCoder Beginner Contest 334 E
        • 问题陈述
        • 思路
    • AtCoder Beginner Contest 333 E
        • 问题陈述
        • 思路
    • AtCoder Beginner Contest 332 D
        • 问题陈述
        • 思路

AtCoder Beginner Contest 336 C

问题陈述

非负整数 n n n 满足下列条件时,称为好整数

  • n n n的十进制符号中的所有数位都是偶数( 0 0 0 2 2 2 4 4 4 6 6 6 8 8 8)。

例如, 0 0 0 68 68 68 2024 2024 2024 都是好整数。

给你一个整数 N N N。请找出 N N N-th最小的好整数。

思路

考察的实质上是简单是进制转换,即十进制转换为五进制,但要求输出 N N N-th最小的好整数,但没有第0小这一说法,所以需要n–,再进行转换。

#include 

using namespace std;
const int N = 5e5+ 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<int, 4> ar;
int mod = 998244353;
const int maxv = 4e6 + 5;
// #define endl "\n"

int a[]={0,2,4,6,8};

void solve() 
{
	ll n;
	cin>>n;
	n--;
	if(n==0){
		cout<<0<<endl;
		return ;
	}
	vector<int> c;
	while(n){
		c.push_back(n%5);
		n/=5;
	}
	reverse(c.begin(),c.end());
	// for(auto x: c) cout<
	for(int i=0;i<c.size();i++){
		cout<<a[c[i]];
	}
	cout<<endl;
}    


int main() 
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	// cin>>t;
	while(t--){
		solve();
	}
   	system("pause");
    return 0;
}

AtCoder Beginner Contest 336 D

问题陈述

对于正整数 k k k,大小为 k k k金字塔序列是长度为 ( 2 k − 1 ) (2k-1) (2k1)的序列,序列中的项依次具有值 1 , 2 , … , k − 1 , k , k − 1 , … , 2 , 1 1,2,\ldots,k-1,k,k-1,\ldots,2,1 1,2,,k1,k,k1,,2,1

给你一个长度为 N N N 的序列 A = ( A 1 , A 2 , … , A N ) A=(A_1,A_2,\ldots,A_N) A=(A1,A2,,AN)
请计算在 A A A上重复选择并执行下列操作之一(可能为零次)所得到的金字塔序列的最大长度。

  • 选择序列中的一个项,并将其值减少 1 1 1
  • 删除第一个或最后一个项。

可以证明,问题的约束条件保证了至少有一个金字塔序列可以通过重复操作得到。

思路

考虑维护前缀和后缀,然后扫一遍即可。

#include 

using namespace std;
const int N = 5e5+ 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<int, 4> ar;
int mod = 998244353;
const int maxv = 4e6 + 5;
// #define endl "\n"



void solve() 
{
	int n;
	cin>>n;
	vector<int> pre(n+5),suf(n+5),a(n+5);
	for(int i=1;i<=n;i++){
		cin>>a[i];
		pre[i]=min(pre[i-1]+1,a[i]);
	}
	for(int i=n;i>=1;i--){
		suf[i]=min(suf[i+1]+1,a[i]);
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		int res=min(pre[i],suf[i]);
		ans=max(ans,res);
	}
	cout<<ans<<endl;

}    


int main() 
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	// cin>>t;
	while(t--){
		solve();
	}
   	system("pause");
    return 0;
}

AtCoder Beginner Contest 335 E

问题陈述

有一个连通的无向图,图中有 N N N 个顶点和 M M M 条边,其中 i i i 条边双向连接顶点 U i U_i Ui 和顶点 V i V_i Vi
每个顶点上都写有一个整数,顶点 v v v上写有整数 A v A_v Av

对于从顶点 1 1 1到顶点 N N N的简单路径(一条不多次通过相同顶点的路径),分数的确定方法如下:

  • 假设 S S S 是写在路径沿线顶点上的整数序列,按照顶点被访问的顺序排列。
  • 如果 S S S不递减,则该路径的得分为 0 0 0
  • 否则,得分就是 S S S 中不同整数的个数。

找出所有简单路径中得分最高的从顶点 1 1 1 到顶点 N N N 的路径,并打印该得分。

思路

这很scc,因为对于一个强连通分量,我们只能算作一个点,首先把双向边变为单向,当 a u ≤ a v a_u \le a_v auav时,连一条 u u u- v v v的边,反之则连 v − u v-u vu,因此所有双向边被我们化为了单向,正因为是单向,所以才能得出,一个强连通分量只能算一个点这个结论,因此缩点,然后dp一下即可。

#include 

using namespace std;
const int N = 5e5+ 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<int, 4> ar;
int mod = 998244353;
const int maxv = 4e6 + 5;
// #define endl "\n"

int n, m, tot, dfsn[N], ins[N], low[N];
stack<int> s;
vector<vector<int>> scc;
vector<int> b(N);
int a[N],z[N];
vector<int> e[N], e2[N];
void dfs(int x)
{
	low[x] = dfsn[x] = ++tot, ins[x] = 1, s.push(x);
	for (auto u : e[x])
	{
		if (!dfsn[u])
		{
			dfs(u);
			low[x] = min(low[x], low[u]);
		}
		else if (ins[u])
			low[x] = min(low[x], dfsn[u]);
	}
	if (dfsn[x] == low[x])
	{
		vector<int> c;
		while (1)
		{
			auto t = s.top();
			c.push_back(t);
			ins[t] = 0;
			s.pop();
			b[t] = scc.size();
			if (t == x)
				break;
		}
		scc.push_back(c);
	}
}


void add(int u,int v)
{
    e[u].push_back(v);
}

void solve() 
{
    memset(z,0x3f,sizeof z);
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=m;i++){
        int u,v;
        cin>>u>>v;
        if(a[u]<=a[v]) add(u,v);
        if(a[u]>=a[v]) add(v,u);
    }
	for (int i = 1; i <= n; i++)
	{
		if (!dfsn[i])
		{
			dfs(i);
		}
	}
	for(int i=1;i<=n;i++){//缩点部分
		for(auto u: e[i]){
			if(b[i]!=b[u]){
				e2[b[i]].push_back(b[u]);
			}
		}
	}
    vector<int> dp(n+5,-1e9);
    dp[b[1]]=1;
    // for(int i=0;i
    //     for(auto x: scc[i]) cout<
    //     cout<
    // }
    for(int i=scc.size()-1;i>=0;i--){
        for(auto u: e2[i]){
            dp[u]=max(dp[u],dp[i]+1);
        }
    }
    cout<<max(0,dp[b[n]])<<endl;

}    


int main() 
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	// cin>>t;
	while(t--){
		solve();
	}
   	system("pause");
    return 0;
}

AtCoder Beginner Contest 334 C

问题陈述

高桥有 N N N双袜子,其中 i i i双由两只颜色为 i i i的袜子组成。一天,高桥在整理抽屉时发现自己丢失了 A 1 , A 2 , … , A K A_1, A_2, \dots, A_K A1,A2,,AK种颜色的袜子各一只,于是他决定用剩下的 2 N − K 2N-K 2NK只袜子做 ⌊ 2 N − K 2 ⌋ \lfloor\frac{2N-K}{2}\rfloor 22NK双新袜子,每双由两只袜子组成。一双颜色为 i i i的袜子和一双颜色为 j j j的袜子的怪异度定义为 ∣ i − j ∣ |i-j| ij,高桥希望将总怪异度最小化。

求用剩下的袜子做成 ⌊ 2 N − K 2 ⌋ \lfloor\frac{2N-K}{2}\rfloor 22NK对时,总奇异度的最小值。请注意,如果 2 N − K 2N-K 2NK是奇数,那么将有一只袜子不包含在任何一对袜子中。

思路

我们贪心的想,需要最小化怪异度,那么我们尽可能的需要将两个编号相近的袜子搭配在一起,再考虑总数为奇数和偶数的情况,当总数为奇数时,我们有一只袜子不需要选取,那么此时我们需要去枚举每一双袜子,然后取怪异度最小的。

我们容易发现,当总数为奇数的时候,我们两两进行匹配,因为天然有序,所以我们移除偶数位的袜子,只会导致答案更差,所以我们只需要维护奇数位的袜子,我们把该位的袜子移除后,对于该位置前的袜子不会产生影响,对于该袜子后面的袜子,即是从最后一个开始,进行两两组合,所以我们维护一遍前缀,维护一遍后缀,最后扫一遍即可。

对于总数为偶数的情况,直接两两进行配对即可。

#include 

using namespace std;
const int N = 5e5+ 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<int, 4> ar;
int mod = 998244353;
const int maxv = 4e6 + 5;
// #define endl "\n"


void solve() 
{
    int n,k;
    cin>>n>>k;
    vector<ll> a(k+5);
    for(int i=1;i<=k;i++) cin>>a[i];
    if(k&1){
        vector<ll> c1(k+5),c2(k+5);
        for(int i=1;i<=k;i++){
            if(i%2==0){
                c1[i]=a[i]-a[i-1]+c1[i-1];
            }
            else c1[i]=c1[i-1];
        }
        for(int i=k;i>=1;i--){
            if(i%2==0){
                c2[i]=a[i+1]-a[i]+c2[i+1];
            }
            else c2[i]=c2[i+1];
        }
        ll ans=1e18;
        for(int i=1;i<=k;i++){
            if(i&1){
                ans=min(ans,c1[i]+c2[i]);
            }
        }
        cout<<ans<<endl;
    }
    else {
        ll ans=0;
        for(int i=1;i<=k;i++){
            if(i%2==0){
                ans+=a[i]-a[i-1];
            }
        }
        cout<<ans<<endl;
    }
}    


int main() 
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	// cin>>t;
	while(t--){
		solve();
	}
   	system("pause");
    return 0;
}

AtCoder Beginner Contest 334 E

问题陈述

有一个行数为 H H H 列数为 W W W 的网格,每个单元格都涂有红色或绿色。

( i , j ) (i,j) (i,j) 表示从上往下第 i i i 行和从左往上第 j j j 列中的单元格。

单元格 ( i , j ) (i,j) (i,j) 的颜色用字符 S i , j S_{i,j} Si,j 表示,其中 S i , j = S_{i,j} = Si,j= . 表示单元格 ( i , j ) (i,j) (i,j). 表示单元格 ( i , j ) (i,j) (i,j) 为红色,而 S i , j = S_{i,j} = Si,j=# 表示单元格 ( i , j ) (i,j) (i,j) 为绿色。

网格中的绿色连通元件数是图形中的连通元件数,顶点集是绿色单元格,边集是连接相邻两个绿色单元格的边。在这里,当 ∣ x − x ′ ∣ + ∣ y − y ′ ∣ = 1 |x-x'| + |y-y'| = 1 xx+yy=1时,两个单元格 ( x , y ) (x,y) (x,y) ( x ′ , y ′ ) (x',y') (x,y)被认为是相邻的。

考虑随机均匀地选择一个单元格,并将其重新涂成绿。打印重新绘制后网格中绿色相连单元格数量的期望值,模为 998244353 998244353 998244353

打印模数为 998244353 998244353 998244353的预期值 "是什么意思?可以证明所求的期望值总是有理数。此外,本题的约束条件保证,如果用两个共价整数 P P P Q Q Q 将该值表示为 P Q \frac{P}{Q} QP,则恰好有一个整数 R R R 可以表示 R × Q ≡ P ( m o d 998244353 ) R \times Q \equiv P \pmod{998244353} R×QP(mod998244353) 0 ≤ R < 998244353 0 \leq R < 998244353 0R<998244353。打印这个 R R R.

思路

搜出来每个联通块,然后计算每个点对于答案的贡献即可,每个点对答案的贡献为连通块总数-该点周围的连通块个数+1.

#include 

using namespace std;
const int N = 5e5+ 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<int, 4> ar;
int mod = 998244353;
const int maxv = 4e6 + 5;
// #define endl "\n"

int n,m;
char a[1005][1005];
bool st[1005][1005];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
int c[1005][1005];

ll qmi(ll a,ll b,ll c)
{
    ll res=1;
    while(b){
        if(b%2){
            res=res*a%c;
        }
        b>>=1;
        a=a*a%c;
    }
    return res;
}
ll inv(int x)
{
    return qmi(x,mod-2,mod);
}

void bfs(int x,int y,int num)
{
    queue<pll> q;
    q.push({x,y});
    st[x][y]=1;
    c[x][y]=num;
    while(!q.empty()){
        auto [nx,ny]=q.front();
        q.pop();
        for(int i=0;i<4;i++){
            int zx=nx+dx[i],zy=ny+dy[i];
            if(zx<1||zx>n||zy<1||zy>m) continue;
            if(a[zx][zy]!='#'||st[zx][zy]) continue;
            st[zx][zy]=1;
            q.push({zx,zy});
            c[zx][zy]=num;
        }
    }
}


void solve() 
{
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++) cin>>a[i][j];
    }
    int num=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(a[i][j]=='#'&&!st[i][j]){
                bfs(i,j,num);
                num++;
            }
        }
    }
    // cout<
    num--;
    int ans=0,cnt=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(a[i][j]=='.'){
                map<int,int> mp;
                for(int k=0;k<4;k++){
                    int nx=i+dx[k],ny=j+dy[k];
                    if(nx<1||nx>n||ny<1||ny>m) continue;
                    if(a[nx][ny]=='.') continue;
                    mp[c[nx][ny]]++;
                }
                // cout<
                ans+=num-(mp.size()-1);
                ans%=mod;
                cnt++;
            }
        }
    }
    ans=ans*inv(cnt)%mod;
    cout<<ans<<endl;
}    


int main() 
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	// cin>>t;
	while(t--){
		solve();
	}
   	system("pause");
    return 0;
}

AtCoder Beginner Contest 333 E

问题陈述

高桥将踏上冒险之旅。

在探险过程中,会发生 N N N 个事件。 i i i-th 事件 ( 1 ≤ i ≤ N ) (1\leq i\leq N) (1iN) 由一对整数 ( t i , x i ) (t _ i,x _ i) (ti,xi) 表示。 ( 1 ≤ t i ≤ 2 , 1 ≤ x i ≤ N ) (1\leq t _ i\leq 2,1\leq x _ i\leq N) (1ti2,1xiN) 并如下所示:

  • 如果 t i = 1 t _ i=1 ti=1,他找到了一个 x i x _ i xi类型的药水。他可以选择捡起或丢弃。
  • 如果是 t i = 2 t _ i=2 ti=2,他将遇到一只 x i x _ i xi 类型的怪物。如果他有 x i x _ i xi型药水,他可以使用一种药水击败怪物。如果他没有打败怪物,他就会被打败。

判断他是否可以打败所有怪物而不被打败。

如果他无法打败所有怪物,则打印 -1

否则,让 K K K成为他在冒险过程中拥有的最大药水数量。让 K min ⁡ K _ {\min} Kmin成为 K K K在所有策略中不会被击败的最小值。打印 K min ⁡ K _ {\min} Kmin的值以及高桥实现 K min ⁡ K _ {\min} Kmin的行动。

思路

我们对于每种药水使用一个数组进行储存,那么当我们遇到怪物时,我们使用最近的一个药水,肯定产生的影响最小,那么若需要使用这瓶药水,则代表我们需要在捡到该药水的位置一直到当前位置,一直持有,换个说法就行,对这一段区间的贡献+1,最后按照这样进行统计即可。

#include 

using namespace std;
const int N = 5e5+ 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<int, 4> ar;
int mod = 998244353;
const int maxv = 4e6 + 5;
// #define endl "\n"


void solve() 
{
   int n;
   cin>>n;
   vector<pll> a(n+5);
   for(int i=1;i<=n;i++){
        int x,y;
        cin>>x>>y;
        a[i]={x,y};
    } 
    vector<int> c[n+5];
    vector<int> d(n+5);
    for(int i=1;i<=n;i++){
        auto [t,x]=a[i];
        if(t==1){
            c[x].push_back(i);
        }
        else{
            if(!c[x].size()){
                cout<<-1<<endl;
                return ;
            }
            auto pos=c[x].back();
            c[x].pop_back();
            d[pos]=1;
            d[i]=-1;
        }
    }
    int ans=0;
    vector<int> s(n+5);
    for(int i=1;i<=n;i++){
        s[i]=s[i-1]+d[i];
        ans=max(ans,s[i]);
    }
    cout<<ans<<endl;
    for(int i=1;i<=n;i++){
        auto [t,x]=a[i];
        if(t==1){
            cout<<d[i]<<" ";
        }
    }
    cout<<endl;

}    


int main() 
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	// cin>>t;
	while(t--){
		solve();
	}
   	system("pause");
    return 0;
}

AtCoder Beginner Contest 332 D

问题陈述

给你两个网格,A 和 B,每个网格有 H H H 行和 W W W 列。

对于满足 1 ≤ i ≤ H 1 \leq i \leq H 1iH 1 ≤ j ≤ W 1 \leq j \leq W 1jW的每一对整数 ( i , j ) (i, j) (i,j),让 ( i , j ) (i, j) (i,j)表示 i i i行和 j j j列中的单元格。在网格 A 中,单元格 ( i , j ) (i, j) (i,j) 包含整数 A i , j A_{i, j} Ai,j。在网格 B 中,单元格 ( i , j ) (i, j) (i,j) 包含整数 B i , j B_{i, j} Bi,j

您将重复以下操作任意多次,可能为零。在每次操作中,您都要执行以下操作之一:

  • 选择一个满足 1 ≤ i ≤ H − 1 1 \leq i \leq H-1 1iH1的整数 i i i,然后交换网格 A 中的 i i i行和 ( i + 1 ) (i+1) (i+1)行。
  • 选择一个满足 1 ≤ i ≤ W − 1 1 \leq i \leq W-1 1iW1的整数 i i i,然后交换网格 A 中的 i i i/th 列和 ( i + 1 ) (i+1) (i+1)/th 列。

确定是否有可能通过重复上述操作使网格 A 与网格 B 相同。如果可能,请打印出这样做所需的最少操作次数。

这里,当且仅当对于满足 1 ≤ i ≤ H 1 \leq i \leq H 1iH 1 ≤ j ≤ W 1 \leq j \leq W 1jW 的所有整数对 ( i , j ) (i, j) (i,j) 来说,写在网格 A 的单元格 ( i , j ) (i, j) (i,j) 中的整数等于写在网格 B 的单元格 ( i , j ) (i, j) (i,j) 中的整数时,网格 A 才与网格 B 相同。

思路

两种方法,一种是暴力枚举所有可能性,然后取最小,另一种是进行bfs搜索。

#include 

using namespace std;
const int N = 5e4 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<int, 4> ar;
int mod = 998244353;
const int maxv = 4e6 + 5;
// #define endl "\n"

int n,m;
int ans=0x3f3f3f3f;
int a[10][10],b[10][10];
vector<int> p(10),q(10);
bool check()
{
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            int x=p[i],y=q[j];
            if(a[x][y]!=b[i][j]) return false;
        }
    }
    return true;
}

void solve() 
{
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>a[i][j];
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>b[i][j];
        }
    }
    for(int i=1;i<=n;i++) p[i]=i;
    for(int i=1;i<=m;i++) q[i]=i;
    do{
        do{
            if(check()){
                int c1=0,c2=0;
                for(int i=1;i<=n;i++){
                    for(int j=i+1;j<=n;j++){
                        if(p[i]>p[j]) c1++;
                    }
                }
                for(int i=1;i<=m;i++){
                    for(int j=i+1;j<=m;j++){
                        if(q[i]>q[j]) c2++;
                    }
                }
                ans=min(ans,c1+c2);
            }
        }while(next_permutation(q.begin()+1,q.begin()+1+m));
    }while(next_permutation(p.begin()+1,p.begin()+1+n));
    if(ans!=0x3f3f3f3f){
        cout<<ans<<endl;
    }
    else cout<<-1<<endl;
}    


int main() 
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	// cin>>t;
	while(t--){
		solve();
	}
   	system("pause");
    return 0;
}
#include 

using namespace std;
const int N = 5e4 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<int, 4> ar;
int mod = 998244353;
const int maxv = 4e6 + 5;
// #define endl "\n"



void solve() 
{
    int n,m;
    cin>>n>>m;
    vector<vector<int> > a(n+5,vector<int>(m+5,0));
    vector<vector<int> > b(n+5,vector<int>(m+5,0));
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++) cin>>a[i][j];
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++) cin>>b[i][j];
    }
    set<vector<vector<int> > > se;
    se.insert(a);
    queue<pair<int,vector<vector<int>>>> q;
    q.push({0,a});
    while(!q.empty()){
        auto [tar,v]=q.front();
        // cout<
        q.pop();
        if(v==b){
            cout<<tar<<endl;
            return ;
        }
        for(int i=1;i<=n-1;i++){
            auto u=v;
            u[i].swap(u[i+1]);
            if(se.find(u)==se.end()){
                se.insert(u);
                q.push({tar+1,u});
            }
        }
        for(int i=1;i<=m-1;i++){
            auto u=v;
            for(int j=1;j<=n;j++){
                swap(u[j][i],u[j][i+1]);
            }
            if(se.find(u)==se.end()){
                se.insert(u);
                q.push({tar+1,u});
            }
        }
    }
    cout<<-1<<endl;
}    


int main() 
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	// cin>>t;
	while(t--){
		solve();
	}
   	system("pause");
    return 0;
}

你可能感兴趣的:(补题记录,算法,图论,c++,数据结构)