Atcoder Beginner Contest 241(A-F)

在 Print 之前

这是 2.20 打的一场 VP 也是非常感人,第四题磕了一个多小时没磕出来,最后终于是过了6道题。

A - Digit Machine

题目

此题也是非常的简单,总共 3 3 3 次操作,每次修改一下 a n s ans ans 即可。

A - Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1e2+5;
ll a[N],ans;
int main() {
    for(ll i=0;i<=9;i++) {
        cin>>a[i];
    }
    ans=a[0];
    ans=a[ans];
    ans=a[ans];
    cout<<ans;
    return 0;
}

B - Pasta

题目

此题更简单,用标记数组标记即可,但 a i a_{i} ai b i b_{i} bi 值过大,所以建议用 m a p map map

B - Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1e3+5;
ll n,m,a[N],b[N];
map<ll,ll> mp;
string s="Yes";
int main() {
    cin>>n>>m;
    for(ll i=1;i<=n;i++) {
        cin>>a[i];
        mp[a[i]]++;
    }
    for(ll j=1;j<=m;j++) {
        cin>>b[j];
        if(mp[b[j]]<=0) {
            s="No";
            break;
        }
        mp[b[j]]--;
    }
    cout<<s;
    return 0;
}

C - Connect 6

题目

题目说要找六个竖着、横着、左下斜着以及右下斜着的连续的 # 有两次修改机会。

这很明显是一个 模拟+dfs

那么,我们可以思考这样一个问题:

我们需要往回搜吗(往左、上、右上、左上)?

不难发现,我们上面的情况是已经搜过的了,没必要再次枚举,所以直接往下搜即可。

结合代码可能会更好理解

C - Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1e3+5;
ll n,cnt;
string s;
bool a[N][N],flag;
void dfs(ll x,ll y,ll num,ll k) {
    bool f=0;
    if(k==0&&a[x][y]==0) return ;
    if(a[x][y]==0) {
        k--; 
        a[x][y]=1;
        f=1;
    }
    cnt++;
    if(cnt>=6) return ;
    if(num==1) {
        if(x<=n&&y+1<=n&&x>=1&&y+1>=1) dfs(x,y+1,num,k);
    }
    if(num==2) {
        if(x+1<=n&&y<=n&&x+1>=1&&y>=1) dfs(x+1,y,num,k);
    }
    if(num==3) {
        if(x+1<=n&&y-1<=n&&x+1>=1&&y-1>=1) dfs(x+1,y-1,num,k);
    }
    if(num==4) {
        if(x+1<=n&&y+1<=n&&x+1>=1&&y+1>=1) dfs(x+1,y+1,num,k);
    }
    if(f==1) {
        a[x][y]=0;
    }
}
int main() {
    cin>>n;
    for(ll i=1;i<=n;i++) {
        cin>>s;
        for(ll j=0;j<n;j++) {
            a[i][j+1]=(s[j]=='#');
        }
    }
    for(ll i=1;i<=n;i++) {
        for(ll j=1;j<=n;j++) {
            if(flag==1) break;
            if(a[i][j]!=0) {
                for(ll k=1;k<=4;k++) {
                    cnt=0;
                    dfs(i,j,k,2);
                    if(cnt>=6) {
                        flag=1;
                        break;
                    }
                }
                continue;
            }
            a[i][j]=1;
            for(ll k=1;k<=4;k++) {
                cnt=0;
                dfs(i,j,k,1);
                if(cnt>=6) {
                    flag=1;
                    break;
                }
            }
            a[i][j]=0;
        }
    }
    if(flag==1) {
        cout<<"Yes";
    }
    else {
        cout<<"No";
    }
    return 0;
}

D - Sequence Query

题目

这题第一眼看时,就是个大模拟+二分,但如果 s o r t sort sort 的话复杂度来到 O ( Q n l o g n ) O(Qnlogn) O(Qnlogn),包超时的

那么如何下手呢。

我们一看数据 :

1 ≤ Q ≤ 2 × 1 0 5 1\le Q\le2\times10^5 1Q2×105

1 ≤ x ≤ 1 0 18 1\le x\le10^{18} 1x1018

1 ≤ k ≤ 5 1\le k\le5 1k5

只有个非常不合群的 k ≤ 5 k\le5 k5,那么我们想办法优化 s o r t sort sort,扩大查询复杂度不变 O ( l o g n ) O(log n) O(logn),不就行了。
这时只要上网一查 m u l t i s e t multiset multiset 好东西,自动排序,复杂度 O ( 1 ) O(1) O(1),这就是为这题量身定做的,就是要思考一下查询:

n u m = 2 num=2 num=2 时,我们需要输出 A A A 中所有 ≤ x \le x x 的元素中的第 k k k 大值。如果不存在输出 -1

这只需要搜到第一个 > x >x >x 的数开始往后编译 k k k,如果 i t = a . b e g i n ( ) it=a.begin() it=a.begin() 时说明无解,否则输出 i t it it

n u m = 3 num=3 num=3 时,我们需要输出 A A A 中所有 ≥ x \ge x x 的元素中的第 k k k 小值。如果不存在输出 -1

这只需要从第一个 ≥ x \ge x x 的数开始遍历,总共遍历 k − 1 k-1 k1,如果 i t = a . e n d ( ) it=a.end() it=a.end() 时说明无解,否则输出 i t it it

D - Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=2e5+5;
ll T,f=0;
multiset<ll> a;
int main() {
    cin>>T;
    while(T--) {
        ll num,x,k;
        cin>>num>>x;
        if(num==1) {
            a.insert(x);
        }
        else {
            cin>>k;
            multiset<ll>::iterator cnt=a.lower_bound(x);
            multiset<ll>::iterator ans=a.upper_bound(x);
            if(num==2) {
                ll op=0;
                auto it=ans;
                for(ll i=1;i<=k;i++) {
                    if(it==a.begin()) {
                        op=1;
                        break;
                    }
                    it--;
                }
                if(op==1) {
                    cout<<-1;
                }
                else {
                    cout<<*it;
                }
            }
            else {
                ll op=0;
                auto it=cnt;
                if(it==a.end()) op=1;
                else {
                    for(ll i=1;i<k;i++) {
                        it++;
                        if(it==a.end()) {
                            op=1;
                            break;
                        }
                    }
                }
                if(op==1) {
                    cout<<-1;
                }
                else {
                    cout<<*it;
                }
            }
            cout<<endl;
        }
    }
    return 0;
}

E - Putting Candies

题目

这是一个很明显的抽屉原理,它在查找一定次数后,就会陷入循环,这样我们只需要遍历 k k k m o d mod mod n n n 次就可以了。

E - Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=2e5+5;
ll a[N],sum[N],num[N],n,t,s,k;
int main() {
	cin>>n>>k;
	for(ll i=0;i<n;i++) {
		cin>>a[i];
    }
	for(ll i=1;i<n;i++) {
        num[i]=-1;
    }
	for(ll i=0;i<n;i++) {
		sum[i+1]=sum[i]+a[sum[i]%n];
		if(num[sum[i+1]%n]!=-1) {
			s=num[sum[i+1]%n];
			t=i+1;
			break;
		}
		num[sum[i+1]%n]=i+1;
	}
	if(k<=s) cout<<sum[k];
	else {
		ll p=t-s;
		ll x=sum[t]-sum[s],pos=k-s-1;
		cout<<sum[s+pos%p+1]+pos/p*x;
	}
	return 0;
}

F - Skate

题目
我借鉴了一下大佬的题解及代码(我的代码太丑了)

这是一道一眼的 b f s bfs bfs 但单单是搜索就很容易爆 T L E TLE TLE

于是我们可以很容易 想到用 s e t set set 记录下每一行和每一列 分别有哪些障碍,这样向四个方向移动是就可以直接二分出该方向最近的障碍即可,可以使用 lower_bound

记录每一行和每一列分别有哪些障碍和从起点到各个点的距离等数据时,若是直接用数组存,显然直接 M L E MLE MLE 炸掉。我们可以用 m a p map map 代替数组存数据。

F - Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll h,w,n,sx,sy,ex,ey,ans=-1;
map<ll,set<ll> > x,y;
map<pair<ll,ll>,ll> d;
queue<pair<ll,ll> > q;
void bfs() {
    q.push({sx,sy});
	d[{sx,sy}]=0;
	while(!q.empty()) {
        pair<ll,ll> pl=q.front();
		ll u=pl.first,v=pl.second;
		q.pop();
		if(u==ex&&v==ey) {
			ans=d[{u,v}];
			break;
		}
		auto it=x[u].lower_bound(v);
		if(it!=x[u].begin()) {
			if(d.find({u,*prev(it)+1})==d.end()) {
				q.push({u,*prev(it)+1});
				d[{u,*prev(it)+1}]=d[{u,v}]+1; 
			}
		}
		if(it!=x[u].end()) {
			if(d.find({u,*it-1})==d.end()) {
				q.push({u,*it-1});
				d[{u,*it-1}]=d[{u,v}]+1; 
			}
		}
		it=y[v].lower_bound(u);
		if(it!= y[v].begin()) {
			if(d.find({*prev(it)+1,v})==d.end()) {
				q.push({*prev(it)+1,v});
				d[{*prev(it)+1,v}]=d[{u,v}]+1; 
			}
		}
		if(it!=y[v].end()) {
			if(d.find({*it-1,v})==d.end()) {
				q.push({*it-1,v});
				d[{*it-1,v}]=d[{u,v}]+1; 
			}
		}
	}
}
int main() {
	cin>>h>>w>>n>>sx>>sy>>ex>>ey;
	for (ll i = 1; i <= n; i++) {
		ll ul,vl;
		cin>>ul>>vl;
		x[ul].insert(vl);
		y[vl].insert(ul);
	}
    bfs();
	cout<<ans;
	return 0;
}

后记

总体来说难度适中,不算太难但也不在舒适圈内,希望下次有进步

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值