2024-05-14-ABC353

ABC353讲解

A - Buildings

题意:给定n个数字,输出第一个大于第一个数的下标。

#include<bits/stdc++.h>
#define rep(i,n) for(int i=0;i<(int)n;i++)
using namespace std;
int main()
{
	int n,a[105];
	cin>>n;
	rep(i,n) cin>>a[i];
	rep(i,n)
	{
		if(!i) continue;
		if(a[i]>a[0])
		{
			cout<<i+1;
			return 0;
		}
	}
	cout<<-1;
	return 0;
}

B - AtCoder Amusement Park

题意:n组,每组若干个人,坐云霄飞车。每个飞车只有k个座位。依次给这n组人安排飞车,若该组人可以坐进飞车,则坐。否则另开一个新的飞车给他们坐。

问最后用了多少个飞车。

#include <bits/stdc++.h>
using namespace std;
int main() {
	int n, k;
	cin >> n >> k;
	int ans = 0;
	int cur = k;
	for (int i = 0; i < n; i++) {
		int x;
		cin >> x;
		if (cur < x) {
			++ans;
			cur = k;
		}
		cur -= x;
	}
	cout << ans + 1 << endl;
	return 0;
}

C - Sigma Problem

题意:求\(∑^{n−1}_{i=1}∑^n_{j=i+1}(a_i+a_j) mod 10^8\)

解法:注意题意是先模再求和。根据数据范围可以看出,\(a_i+a_j\)一定小于\(2 \times 10^8\),即\((a_i+a_j) mod 10^8\)等于\(a_i+a_j\)\(a_i+a_j-10^8\)

那么我们先将数组排序,在遍历每一个数时,二分出其和大于\(10^8\)的分界线,两段利用前缀和分别计算答案即可。

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
 
int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    int mo = 1e8;
    vector<LL> a(n);
    for (auto& x : a)
        cin >> x;
    sort(a.begin(), a.end());
    vector<LL> presum(n);
    partial_sum(a.begin(), a.end(), presum.begin());
    LL ans = 0;
    LL sum = 0;
    for (int i = 0; i < n; ++i) {
        LL bu = mo - a[i];
        if (a[i] < bu) {
            ans += a[i] * i + sum;
        } else {
            auto pos = lower_bound(a.begin(), a.begin() + i, bu) - a.begin();
            LL p = pos > 0 ? presum[pos - 1] : 0;
            ans += a[i] * pos + p;
            LL suf = sum - p;
            ans += suf - bu * (i - pos);
        }
        sum += a[i];
    }
    cout << ans << '\n';
 
    return 0;
}
 

D - Another Sigma Problem

题意:求\(∑^{n−1}_{i=1}∑^n_{j=i+1}f(a_i,a_j) mod 998244353\),其中\(f(a,b)\)为把\(a,b\)两数拼接起来。

解法:这题与上题不同在是求总和的模,那么我们直接考虑枚举到\(a_i\)\(∑^n_{j=i+1}f(a_i,a_j)\)

\(a_j\)每个数作为低位的时候,他们会直接加上自己的数值,即\(i\)的后缀和。

\(a_i\)作为高位的时候,每次会先乘上\(10^{低位位数}\)再加到答案里,那么总和来看就是\(10^{低位位数}\)的后缀和。

所以我们从后向前枚举,维护两个后缀和即可。

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
 
const int mo = 998244353;
 
int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    vector<int> a(n);
    for (auto& x : a)
        cin >> x;
    LL sum = 0;
    LL ten = 0;
    auto calc_ten = [&](int x) {
        int cnt = 0;
        while (x) {
            ++cnt;
            x /= 10;
        }
        LL val = 1;
        while (cnt--)
            val *= 10;
        return val;
    };
    for (int i = n - 1; i >= 0; --i) {
        sum += 1ll * a[i] * i % mo;
        sum += 1ll * a[i] * ten % mo;
        sum %= mo;
        LL val = calc_ten(a[i]);
        ten += val;
        ten %= mo;
    }
    cout << sum << '\n';
 
    return 0;
}
 

E - Yet Another Sigma Problem

题意:求\(∑^{n−1}_{i=1}∑^n_{j=i+1}lcp(s_i,s_j)\),lcp意为最长公共前缀。

解法:将所有字符串压进字典树,每个节点上存有多少个字符串,然后对每个个数大于2的点求和即可。

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
 
const int SZ = 26;
template <typename T, typename K> struct Trie {
    struct node {
        K value;
        bool is_terminal;
        int vis_count;
        array<int, SZ> children;
 
        node(K val) : value(val) {
            is_terminal = false;
            children.fill(0);
            vis_count = 0;
        }
    };
 
    int cast(K val) {
        int ret = val - 'a';
        assert(ret < SZ and ret >= 0);
        return ret;
    }
 
    vector<node> tree;
 
    Trie(K val) { tree.push_back(node(val)); }
 
    void insert(const T& sequence) {
        int cur = 0;
        for (int i = 0; i < (int)sequence.size(); i++) {
            K value = sequence[i];
            if (tree[cur].children[cast(value)] == 0) {
                tree[cur].children[cast(value)] = (int)tree.size();
                tree.emplace_back(value);
            }
            cur = tree[cur].children[cast(value)];
            tree[cur].vis_count += 1;
        }
        tree[cur].is_terminal = true;
    }
 
    LL dfs(int cur) {
        LL sum = 0;
        for (int i = 0; i < SZ; i++) {
            if (tree[cur].children[i] == 0)
                continue;
            int child_node = tree[cur].children[i];
            sum += dfs(child_node);
        }
        sum += 1ll * tree[cur].vis_count * (tree[cur].vis_count - 1) / 2;
        return sum;
    }
};
 
int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    Trie<string, char> tree('a');
    for (int i = 0; i < n; i++) {
        string s;
        cin >> s;
        tree.insert(s);
    }
    LL ans = tree.dfs(0);
    cout << ans << '\n';
 
    return 0;
}
 

F - Tile Distance

题意:给定一个形如下图的地图,L是大瓷砖,S是小瓷砖,每一个小瓷砖块是\(K \times K\)​, 从一块瓷砖走到另一块瓷砖要花1的代价,请问从起点到终点最少要花多少代价。

image-20240514100718913

解法:分类讨论

首先判掉直接曼哈顿距离走过去的 case。

否则可以发现,一定是从 S 径直走到一个大格子,通过若干大格子,最后径直走到 T

可以发现,我们最少要花费 2 的代价,从一个大格子走到(对角)相邻的大格子。

在考虑其他的移动方式:从一个大格子走到隔着一堆小格子的大格子,需要花费 k+1 的代价。当且仅当k≤2时是较优的。

那么枚举一下S,T 相邻的大格子,然后算一下距离即可,这部分是简单的。

#include<bits/stdc++.h>
#define ll long long
#define PB(x,y) push_back({x,y})
#define MP make_pair
#define fi first
#define se second
using namespace std;

ll k,sx,sy,tx,ty;
vector<pair<pair<ll,ll>,ll> >tg1,tg2;
ll dis(pair<ll,ll>x,pair<ll,ll>y)
{
	ll dx=abs(x.fi-y.fi),dy=abs(x.se-y.se);
	if(k==2)return 2*min(dx,dy)+3*abs(dx-dy)/2;
	return 2*max(dx,dy);
}

int main()
{
	ios::sync_with_stdio(false),cin.tie(nullptr);
	
	cin>>k>>sx>>sy>>tx>>ty;
	if(k==1)return cout<<abs(sx-tx)+abs(sy-ty),0;
	if((sx/k+sy/k)&1)tg1.PB(MP(sx/k,sy/k),0);//起点在大方格中
	else
	{
		tg1.PB(MP(sx/k-1,sy/k),sx%k+1);//起点在小方格中,枚举周围的大方格
		tg1.PB(MP(sx/k+1,sy/k),k-sx%k);
		tg1.PB(MP(sx/k,sy/k-1),sy%k+1);
		tg1.PB(MP(sx/k,sy/k+1),k-sy%k);
	}
	if((tx/k+ty/k)&1)tg2.PB(MP(tx/k,ty/k),0);
	else
	{
		tg2.PB(MP(tx/k-1,ty/k),tx%k+1);
		tg2.PB(MP(tx/k+1,ty/k),k-tx%k);
		tg2.PB(MP(tx/k,ty/k-1),ty%k+1);
		tg2.PB(MP(tx/k,ty/k+1),k-ty%k);
	}
	ll ans=abs(sx-tx)+abs(sy-ty);
	for(auto i:tg1)
		for(auto j:tg2)
			ans=min(ans,dis(i.fi,j.fi)+i.se+j.se);
	cout<<ans;
	
	return 0;
}
posted @ 2024-11-30 23:27  ~清渠~  阅读(0)  评论(0编辑  收藏  举报