//https://img2018.cnblogs.com/blog/1646268/201908/1646268-20190806114008215-138720377.jpg

2023.8.19 洛谷普及组月赛解题报告

2023.8.19 洛谷普及组月赛解题报告

【LGR-155-Div.3】洛谷基础赛 #3 &「NnOI」Round 2 - 比赛详情 - 洛谷

100 + 100 + 100 + 60 = 360

T1

签到题。

\(T\) 只有一个,所以我们可以直接处理所有的气球。

气球在 \(T\) 时刻的高度就是 \((T-t_{i}) \times v_{i}\),然后找到 \(\max\) 的值并记录下标即可。

复杂度为 \(O(n)\)

#include <bits/stdc++.h>

#define int long long
#define N 1000100

using namespace std;

int n, m, a[N], t[N], maxn = -1, ans;

signed main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i ++)
    {
        int t, v, res;
        cin >> v >> t;
        res = (m - t) * v;
        if(res > maxn) maxn = res, ans = i;
    }
    cout << ans << endl;
    return 0;
}

T2

简单小构造。

发现如果要是串中 \(N\) 的数量大于 \(n\),那么就是无解,因为那时所有的冰川都融化了。

然后我们对于 \(Y\),发现要字典序最小,那么我们直接顺序从 \(1\)\(n\),遇到一个 \(N\) 就输出当前最小的没有融化的冰川,遇到一个 \(Y\) 就可以直接输出 \(1\),因为字典序最小。

或许可以加个每个冰川融化 \(v_{i}\) 次后就没了(?

复杂度为 \(O(m)\)

T3

我们开一个 map 来统计线段中 \(k,b\) 的出现情况。

然后用结构体来存放直线,准备两个结构体数组,用于删除操作。

对于操作一,我们直接加入,在加入的时候统计一下 \(k,b\) 的出现情况。

对于操作二,由于要求恰好一个交点的直线条数,那么我们直接把除了平行以外的所有直线的条数输出即可,也就是下面代码里的 \(top-mp1_{k}\)

对于操作三,我们直接遍历当前所有的直线然后把不删的存到另一个结构体数组里面,然后再复制回去,同时重新统计 \(k,b\) 的信息即可。

但是这样操作三复杂度太高,考虑到有可能删完当前所有直线信息不变。

我们开一个数组 \(vis\),为 \(1\) 表示当前斜率也就是 \(k\) 值的直线删除过了,\(0\) 则是修改过但是没删除过。

我们考虑到每次的 \(b\) 可能不同,但是没出现过的 \(b\) 的线段要是 \(vis_{k}=1\) 也同样没有必要删除,所以最后得到清空的条件为 \(vis_{i} \ne 1\) 并且 \(mp2_{b}\ne 0\)

code:

#include <bits/stdc++.h>

#define int long long
#define N 1000100
#define endl '\n'

using namespace std;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

unordered_map<int, int> mp1, mp2;
int n, ans, top, vis[N];
struct sb{int k, b;}e[N], e1[N];

signed main()
{
    n = read();
    for(int i = 1; i <= n; i ++)
    {
        int op = read(), k = read(), b = read();
        if(op == 1)
        {
            e[++ top] = (sb){k, b};
            mp1[k] ++;
            mp2[b] ++;
            vis[k + 100000] = 0;
        }
        if(op == 2) cout << top - mp1[k] << endl;
        if(op == 3)
        {
            if(vis[k + 100000] == 1 && !mp2[b]) continue;
            int top1 = 0, ff = 0;
            for(int i = 1; i <= top; i ++)
                if(e[i].k == k && e[i].b != b)
                    e1[++ top1] = (sb){e[i].k, e[i].b}, ff ++;
            if(ff == top) continue;
            mp1.clear();
            mp2.clear();
            top = top1;
            vis[k + 100000] = 1;
            for(int i = 1; i <= top; i ++) e[i] = e1[i], mp1[e[i].k] ++, mp2[e[i].b] ++;
        }
    }
    return 0;
}

T4

这道差 40 pts AK 这场模拟赛。

60 pts:

想到要最长公共子序列长度最大,而且 \(S\) 能无限复制,那么我们可以直接把 \(T\) 里在 \(S\) 中出现的都挑出来,所以第一问解决。

对于第二问,没有好的方法直接枚举,然后 \(S\) 串不断循环,直到 \(T\) 空了为止,中间有个优化是把每一个数出现的最后一个位置标记,然后判断是否再接一个 \(S\) 串可以直接省不少复杂度。

#include <bits/stdc++.h>

// #define int long long
#define N 1000100

using namespace std;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

int n, m, a[N], b[N], cnta[N], ans, t = 1, c1, c2, now, last[N], cntb[N], cnt, e[N], top;

signed main()
{
    srand(time(0));
    n = read(), m = read(), c1 = read(), c2 = read();
    for(int i = 1; i <= n; i ++)
    {
        a[i] = read();
        cnta[a[i]] ++;
        last[a[i]] = i;
    }
    for(int i = 1; i <= m; i ++) b[i] = read(), cntb[b[i]] ++;
    for(int i = 1; i <= n; i ++) e[i] = a[i]; top = n;
    for(int i = 1; i <= m; i ++)
    {
        if(!cnta[b[i]]) continue;
        now ++;
        if(now == top + 1) now = 1, t ++;
        if(last[b[i]] < now) now = 1, t ++;
        while(e[now] != b[i])
        {
            now ++;
            if(now == top + 1) now = 1, t ++;
        }
        cntb[b[i]] --;
        ans ++;
        // if(cntb[b[i]] == 0)
        // {
        //     n = top;
        //     top = 0;
        //     for(int i = 1; i <= n; i ++)
        //         if(cntb[e[i]]) a[++ top] = e[i];
        //     for(int i = 1; i <= top; i ++)
        //         e[i] = a[i], last[e[i]] = i;
        // }
    }
    if(ans == 0) t = 0;
    cout << ans * c1 << " " << t * c2 << endl;
    return 0;
}

最后一个点 TLE,不然 AK 了。

100 pts:

考虑优化找下一个与当前值匹配的过程。

预处理所有值出现的位置,到了当前点就去数组里二分一下找第一个大于当前位置的值。

复杂度 \(O(m \log m)\)

其实也不难但是当时就是没想出来。

#include <bits/stdc++.h>

#define int long long
#define N 1000100

using namespace std;

inline int read(){int x=0,f=1;char ch=getchar();while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}return x*f;}

int n, m, c1, c2, s[N], t[N], vis[N];
vector<int> g[N];

inline int findx(int p, int val)
{
	int l = -1, r = g[p].size();
	while(l + 1 < r)
	{
		int mid = (l + r) >> 1;
		if(g[p][mid] > val) r = mid;
		else l = mid;
	}
	return r;
}

signed main()
{
	n = read(), m = read(), c1 = read(), c2 = read();
	for(int i = 1; i <= n; i ++) s[i] = read(), vis[s[i]] = 1;
	for(int i = 1; i <= m; i ++) t[i] = read();
	for(int i = 1; i <= m; i ++)
		if(!vis[t[i]]) t[i] = -1;
	int m1 = m;
	m = 0;
	for(int i = 1; i <= m1; i ++)
		if(t[i] != -1) m ++, t[m] = t[i];
	if(m == 0) return cout << "0 0" << endl, 0;
	for(int i = 1; i <= n; i++) g[s[i]].push_back(i);
	int sc = 1;
	int pos = g[t[1]][0];
	for(int i = 2; i <= m; i ++)
	{
		pos = findx(t[i], pos);
		if(pos == g[t[i]].size()) pos = g[t[i]][0], sc++;
		else pos = g[t[i]][pos];
	}
	cout << m * c1 << " " << sc * c2 << endl;
	return 0;
}

.

posted @ 2023-08-19 20:14  北烛青澜  阅读(35)  评论(0)    收藏  举报