AtCoder Beginner Contest 393(A~F题解)

AtCoder Beginner Contest 393题解分析

A - Poisonous Oyster

思路:一个简单的逻辑题吧,第一个人吃了1,2两种牡蛎,第二个人吃了1,3两种牡蛎,既然四个牡蛎里面只有一个有毒,那么可以分四种情况

1.都良好,4牡蛎有毒

2.第一个人良好,第二个人中毒,3牡蛎有毒

3.第一个人中毒,第二个人良好,2牡蛎有毒

4.两个人都中毒,1牡蛎有毒

#include<bits/stdc++.h>
using namespace std;
#define int long long
int t;
int n,k;
int a[200005];
string s1,s2;
void solve()
{
	cin>>s1;
	cin>>s2;
	if(s1=="sick"&&s2=="sick")
	{
		cout<<1;
	}
	else if(s1=="fine"&&s2=="fine")
	{
		cout<<4;
	}
	else if(s1=="fine"&&s2=="sick")
	{
		cout<<3;
	}
	else
	{
		cout<<2;
	}
}

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

 B - A..B..C

这题的解题关键在于S的长度最大为100,因此我们可以考虑写一个三层循环的代码复杂度最高33*33*34<1e8肯定是可以跑过去的数据的

因此我们可以用a数组去统计'A'出现的位置,b数组去统计'B'出现的位置,c数组去统计'C'出现的位置,然后写一个三重循环,去判断j-i是否等于k-j,同时要满足i<j<k这个条件,然后如果满足就ans++,最后输出ans就可以了

#include<bits/stdc++.h>
using namespace std;
#define int long long
int t;
int n,k;
vector<int> a;
vector<int> b;
vector<int> c;
string s;
void solve()
{
	cin>>s;
	for(int i=0;i<s.size();i++)
	{
		if(s[i]=='A')
		{
			a.push_back(i);
		}
		else if(s[i]=='B')
		{
			b.push_back(i);
		}
		else if(s[i]=='C')
		{
			c.push_back(i);
		}
	}
	int ans=0;
	for(int i:a)
	{
		for(int j:b)
		{
			for(int k:c)
			{
				if(j-i==k-j&&i<j&&j<k)
				{
					ans++;
				}
			}
		}
	}
	cout<<ans;
}

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

 C - Make it Simple

思路:就是说给你n个顶点,m条边,让你去掉自环和重复出现的边,一开始没看清题以为是最小生成树,浪费了我五分钟 

如果u==v那么就是自环,我们的统计数ans++,但是如何去统计重复边呢?我们可以用map<pair<int,int>,int>来判断某条边是否出现过 ,如果此时mp[{u,v}]==0&&mp[{v,u}]==0,那就说明这条边没有出现过,此时将这两种map都标记为1,然后如果有一个不为0,那么就可以说明是重边,也需要ans++,最后输出ans就可以了

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

int t;  
int n,m;  
vector<int> e[200005];  
int f[200005];  

map<pair<int,int>,int> mp;
void solve()  
{  
	cin >> n >> m;  
	for(int i = 1; i <= n; i++)  
	{  
		f[i] = i;    
	}  
	int ans = 0;  
	for(int i = 1; i <= m; i++)  
	{  
		int u, v;  
		cin >> u >> v;  
		if(u==v)
		{
			ans++;
			continue;
		}
		if(mp[{u,v}]==0&&mp[{v,u}]==0)
		{
			mp[{u,v}]=1;
			mp[{v,u}]=1;
		}
		else
		{
			ans++;
		}
	}  
	cout << ans << "\n";   
}  

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

D - Swap to Gather

思路:其实这题一上来就会想到往中间靠的思路,我们只需要找到最中间的位置,然后让两边往中间的1靠就可以,然后就是正常写式子就过了,这个也不怎么需要思考

#include<bits/stdc++.h>
using namespace std;
#define int long long
int t;
int n,k;
int a[200005];
string s;
vector<int> p;
void solve()
{
	cin>>n;
	cin>>s;
	s=" "+s;
	for(int i=1;i<=n;i++)
	{
		if(s[i]=='1')
		{
			p.push_back(i);
		}
	}
	int pos=(p.size()+1)/2-1;
	int ans=0;
	for(int i=0;i<p.size();i++)
	{
		if(i<=pos)
		ans+=p[pos]-(pos-i)-p[i];
		else
		{
			ans+=abs(p[pos]+(i-pos)-p[i]);
		}
	}
	cout<<ans<<"\n";
}

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

E - GCD of Subset

思路:我们其实看到这题就知道时间复杂度最高为O(nlogn)了,我们不妨用一下这种思路,

f[i]数组表示,存进去的都是i的因子

cnt[i]表示,i这个因子出现的数量

ans[i]表示,一定选取a[i]元素的最大gcd的值

我们去预处理每个数的因子,然后碰到a[i]后去统计这些数的因子的数量,最后如果某因子的数量>=k,那么就将其倍数的ans数组赋值为该因子,最后输出ans数组即可

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k;
int a[2000000];
int cnt[1000005];
int ans[1000005];
vector<int> f[1000005];
signed main()
{
	for(int i=1;i<=1e6;i++)
	{
		for(int j=i;j<1000005;j+=i)
		{
			f[j].push_back(i);
		}
	}
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	for(int i=1;i<=n;i++)
	{
		for(int j:f[a[i]])
		{
			cnt[j]++;
		}
	}
	for(int i=1;i<1000005;i++)
	{
		if(cnt[i]>=k)
		{
			for(int j=i;j<1000005;j+=i)
			{
				ans[j]=i;
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		cout<<ans[a[i]]<<"\n";
	}
	return 0;
}

F - Prefix LIS Query

思路:标准的LIS板题,只需要去维护后缀max即可,上面的线段树是来自群里大神的板子

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

struct SegmentTree {
    int size;
    vector<int> tree;

    SegmentTree(int n) {
        size = 1;
        while (size < n) size <<= 1;
        tree.assign(2 * size, 0);
    }

    void update(int idx, int val, int x, int lx, int rx) {
        if (rx - lx == 1) {
            tree[x] = max(tree[x], val);
            return;
        }
        int mid = (lx + rx) / 2;
        if (idx < mid) {
            update(idx, val, 2 * x + 1, lx, mid);
        } else {
            update(idx, val, 2 * x + 2, mid, rx);
        }
        tree[x] = max(tree[2 * x + 1], tree[2 * x + 2]);
    }

    void update(int idx, int val) {
        update(idx, val, 0, 0, size);
    }

    int query(int l, int r, int x, int lx, int rx) {
        if (l >= rx || r <= lx) return 0;
        if (l <= lx && rx <= r) return tree[x];
        int mid = (lx + rx) / 2;
        return max(query(l, r, 2 * x + 1, lx, mid), query(l, r, 2 * x + 2, mid, rx));
    }

    int query(int l, int r) {
        return query(l, r, 0, 0, size);
    }
};

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int N, Q;
    cin >> N >> Q;
    vector<int> A(N);
    for (int i = 0; i < N; ++i) {
        cin >> A[i];
    }

    vector<int> sorted_A = A;
    sort(sorted_A.begin(), sorted_A.end());
    sorted_A.erase(unique(sorted_A.begin(), sorted_A.end()), sorted_A.end());

    auto get_rank = [&](int x) {
        return lower_bound(sorted_A.begin(), sorted_A.end(), x) - sorted_A.begin();
    };


    vector<tuple<int, int, int>> queries(Q);
    for (int i = 0; i < Q; ++i) {
        int R, X;
        cin >> R >> X;
        queries[i] = {R - 1, X, i};
    }

    sort(queries.begin(), queries.end());

    SegmentTree st(sorted_A.size());

    vector<int> ans(Q);
    int ptr = 0;
    for (int i = 0; i < N; ++i) {
        int rank = get_rank(A[i]);
        int max_len = st.query(0, rank) + 1;
        st.update(rank, max_len);
        while (ptr < Q && get<0>(queries[ptr]) == i) {
            int X = get<1>(queries[ptr]);
            int query_rank = get_rank(X + 1); 
            ans[get<2>(queries[ptr])] = st.query(0, query_rank);
            ptr++;
        }
    }

    for (int i = 0; i < Q; ++i) {
        cout << ans[i] << '\n';
    }

    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值