NOI 春季测试题解

T1. 涂色游戏

记录每一行和每一列最后一次修改的时间戳。

对于 \((i,j)\),我们取第 \(i\) 行和第 \(j\) 列最晚的时间戳,就是它的颜色。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define max(x...) max({x})
#define min(x...) min({x})
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
inline int rd()
{
    int sign = 1, re = 0; char c = getchar();
    while(!isdigit(c)){if(c == '-') sign = -1; c = getchar();}
    while(isdigit(c)){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
const int N = 1e5 + 5;
int n, m, q, x[N], y[N], cl[N];
inline void solve()
{
    n = rd(), m = rd(); q = rd();
    FOR(i, 1, n) x[i] = 0;
    FOR(i, 1, m) y[i] = 0;
    FOR(i, 1, q)
    {
        int opt = rd(), p = rd();
        cl[i] = rd();
        if(opt) y[p] = i;
        else x[p] = i;
    }
    FOR(i, 1, n)
    {
        FOR(j, 1, m) printf("%d ", x[i] > y[j] ? cl[x[i]] : cl[y[j]]);
        puts("");
    }
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    int T = rd();
    while(T--) solve();
    return 0;
}

T2. 幂次

初始想法:

我们考虑枚举底数 \(a\)

一个显然的结论:如果 \(a\) 能被表示成 \(a=x^b(b>1)\),那么它不能再作为底数进行计算答案。

对于 \(k\ge 3\),显然 \(a\le 1e6\),因此我们只需要枚举到 \(1e6\) ,每个 \(a\) 操作 \(k\) 次不到,并用 unordered_map 标记 \(\le 1e6\) 的数即可。

对于 \(k=2\),我们发现,对于 \(1e6 < a \le 1e9\) 的底数,它只能贡献到 \(a^2\),设这样的数的个数为 \(p\),因此我们依旧是枚举到 \(1e6\),再标记 \(\le 1e6\) 的数的同时,我们把 \((1e6,1e9]\) 之间的数从 \(p\) 中减去,最后答案加上 \(p\) 即可。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define max(x...) max({x})
#define min(x...) min({x})
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
inline LL rd()
{
    LL sign = 1, re = 0; char c = getchar();
    while(!isdigit(c)){if(c == '-') sign = -1; c = getchar();}
    while(isdigit(c)){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
LL n; int k;
unordered_map<LL, bool> f;
LL sum = 1;
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    n = rd(), k = rd();
    if(k == 1) return printf("%lld", n), 0;
    if(k >= 60) return puts("1"), 0;
    if(k > 2 || n <= 1e12)
    {
        FOR(i, 2, 1e6) if(!f.count(i))
        {
            if(i > n) break;
            LL t = i; bool ok = 1;
            FOR(j, 2, k)
            {
                if((__int128)t * i > n) { ok = 0; break; }
                if(t <= 1e6) f[t] = 1;
                t *= i;
            }
            if(!ok) break;
            FOR(j, k, 59)
            {
                sum++;
                if(t <= 1e6) f[t] = 1;
                if((__int128)t * i > n) break;
                t *= i;
            }
        }
        printf("%lld", sum);
        return 0;
    }
    LL B = sqrt(n), p = B - 1e6;
    FOR(i, 2, 1e6) if(!f.count(i))
    {
        LL t = 1ll * i * i;
        FOR(j, 2, 59)
        {
            sum++;
            if(t <= 1e6) f[t] = 1;
            if(1e6 < t && t <= B) p--;
            if((__int128)t * i > n) break;
            t *= i;
        }
    }
    printf("%lld", sum + p);
    return 0;
}

但开始写的时候忘了只是标记 \(\le 1e6\) 的了,导致被卡 unordered_map(实际上可以过),因此有了更优的做法:

我们换个思路,考虑枚举指数 \(b\)

我们会发现,对于两个数 \(p_1,p_2\),他们统计过的答案的重复部分,就是 \(b=p_1\times p_2\) 的答案。例如 \(4^3=64,~8^2=64,~2^6=64\)

然后这其实就是一个莫比乌斯函数,因此我们只需要求出每个 \(b\) 的答案的时候,暴力更新 \(\mu\) 即可。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define max(x...) max({x})
#define min(x...) min({x})
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
inline LL rd()
{
    LL sign = 1, re = 0; char c = getchar();
    while(!isdigit(c)){if(c == '-') sign = -1; c = getchar();}
    while(isdigit(c)){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
LL n; int k, mu[65];
LL sum = 1, ans;
bool chk(LL a, int b)
{
    LL t = 1;
    FOR(i, 1, b)
    {
        if((__int128)t * a > n) return 0;
        t *= a;
    }
    return 1;
}
inline void Div(int b)
{
    LL l = 1, r = ans;
    while(l <= r)
    {
        LL mid = (l + r) >> 1;
        if(chk(mid, b)) l = mid + 1, ans = mid;
        else r = mid - 1;
    }
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    n = rd(), k = rd(); ans = n;
    if(k == 1) return printf("%lld", n), 0;
    if(k >= 60) return puts("1"), 0;
    FOR(i, k, 59)
    {
        Div(i); sum += (ans - 1) * (mu[i] + 1);
        for(int j = i + i; j < 60; j += i) mu[j] -= mu[i] + 1;
    }
    printf("%lld", sum);
    return 0;
}

T3. 圣诞树

先抛出一个结论:如果连线有交,显然不优。

因此,我们连的线一定是凸包的连续的某一段,但不一定是一直是顺时针或逆时针方向,可以交替进行。

因此我们设计一个状态:\(f[l][r][0/1]\) 表示已经将 \(l\sim r\) 的点连起来,当前位置是在 \(l/r\) 上。

然后我们可以得到转移:

\[f[l][r][0]=\min\{f[l+1][r][0]+dis(l,l+1),f[l+1][r][1]+dis(l,r)\} \]

\[f[l][r][1]=\min\{f[l][r-1][0]+dis(l,r),f[l][r-1][1]+dis(r-1,r)\} \]

答案取 \(\min\{f[1][n-1][0]+dis(1, n),f[1][n-1][1]+dis(n-1,n)\}\)

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define max(x...) max({x})
#define min(x...) min({x})
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
inline int rd()
{
    int sign = 1, re = 0; char c = getchar();
    while(!isdigit(c)){if(c == '-') sign = -1; c = getchar();}
    while(isdigit(c)){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
int n, pos;
struct Node { double x, y; int i; } p[1005], rp[1005];
inline double dis(Node x, Node y)
{
    return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y));
}
double f[1005][1005][2]; int from[1005][1005][2];
void print(int l, int r, int o)
{
    if(l == r) return void(printf("%d ", p[l].i));
    if(o) printf("%d ", p[r].i), print(l, r - 1, from[l][r][o]);
    else printf("%d ", p[l].i), print(l + 1, r, from[l][r][o]);
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    n = rd();
    FOR(i, 1, n)
        scanf("%lf%lf", &p[i].x, &p[i].y), p[i].i = i;
    pos = 1;
    FOR(i, 2, n) if(p[i].y > p[pos].y) pos = i;
    if(pos != n)
    {
        rp[n] = p[pos];
        for(int i = 1, j = pos % n + 1; i < n; i++, j = j % n + 1)
            rp[i] = p[j];
        FOR(i, 1, n) p[i] = rp[i];
    }
    FOR(len, 2, n - 1) for(int i = 1, j = len; j < n; i++, j++)
    {
        f[i][j][0] = f[i][j][1] = 1e18;
        if(f[i][j][0] > f[i + 1][j][0] + dis(p[i], p[i + 1]))
        {
            f[i][j][0] = f[i + 1][j][0] + dis(p[i], p[i + 1]);
            from[i][j][0] = 0;
        }
        if(f[i][j][0] > f[i + 1][j][1] + dis(p[i], p[j]))
        {
            f[i][j][0] = f[i + 1][j][1] + dis(p[i], p[j]);
            from[i][j][0] = 1;
        }
        if(f[i][j][1] > f[i][j - 1][0] + dis(p[j], p[i]))
        {
            f[i][j][1] = f[i][j - 1][0] + dis(p[j], p[i]);
            from[i][j][1] = 0;
        }
        if(f[i][j][1] > f[i][j - 1][1] + dis(p[j], p[j - 1]))
        {
            f[i][j][1] = f[i][j - 1][1] + dis(p[j], p[j - 1]);
            from[i][j][1] = 1;
        }
    }
    printf("%d ", p[n].i);
    if(f[1][n - 1][0] + dis(p[1], p[n]) < f[1][n - 1][1] + dis(p[n - 1], p[n])) print(1, n - 1, 0);
    else print(1, n - 1, 1);
    return 0;
}

T4. 密码锁

首先,我们求出全局最大值和最小值所在的位置。

显然,全局最大值不能和全局最小值放在一起。

因此我们考虑二分一个答案 \(mid\),然后将全局最小值固定在第一行,枚举权值最大值所在的行 \(s\)

对于第 \(i\) 个锁,我们先判断 \(a[i][1]\) 与全局最小值的差是否 \(\le mid\),以及 \(a[i][s]\) 与全局最大值的差是否 \(\le mid\)。如果都满足,就将剩下的两行的值记下来。然后循环移位重复上面的操作。

现在问题就变成了:二维平面上有若干个点,点有颜色(代表是从第几个锁来的),问是否存在一个位置,使得 \(mid\times mid\) 的矩形中包含所有颜色的点。

这个可以用扫描线+线段树进行维护。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define max(x...) max({x})
#define min(x...) min({x})
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
inline int rd()
{
    int sign = 1, re = 0; char c = getchar();
    while(!isdigit(c)){if(c == '-') sign = -1; c = getchar();}
    while(isdigit(c)){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
int n, k, Mx[50005];
int a[50005][5];
int mnp, mxp;
int cnt[50005];
#define pii pair<int, int>
vector<pii> pos[30005];
multiset<int> ss[30005];
namespace Seg
{
    #define ls (now << 1)
    #define rs (now << 1 | 1)
    int tr[200005], tag[200005];
    void build(int now, int l, int r)
    {
        tr[now] = tag[now] = 0;
        if(l == r) return;
        int mid = (l + r) >> 1;
        build(ls, l, mid), build(rs, mid + 1, r);
    }
    inline void dn(int now)
    {
        if(tag[now])
        {
            tag[ls] += tag[now], tr[ls] += tag[now];
            tag[rs] += tag[now], tr[rs] += tag[now];
            tag[now] = 0;
        }
    }
    void modify(int now, int l, int r, int L, int R, int val)
    {
        if(L <= l && r <= R)
        {
            tr[now] += val;
            tag[now] += val;
            return;
        }
        int mid = (l + r) >> 1; dn(now);
        if(L <= mid) modify(ls, l, mid, L, R, val);
        if(mid < R) modify(rs, mid + 1, r, L, R, val);
        tr[now] = max(tr[ls], tr[rs]);
    }
} using namespace Seg;
inline void solve()
{
    n = rd();
    FOR(j, 1, k) FOR(i, 1, n)
        a[i][j] = rd();
    if(k == 1)
    {
        int Mn = 3e4 + 1, Mx = 0;
        FOR(i, 1, n) Mn = min(Mn, a[i][1]), Mx = max(Mx, a[i][1]);
        return void(printf("%d\n", Mx - Mn));
    }
    FOR(i, 1, n)
    {
        int Mn = min_element(a[i] + 1, a[i] + k + 1) - a[i];
        if(Mn > 1) rotate(a[i] + 1, a[i] + Mn, a[i] + k + 1);
    }
    if(k == 2)
    {
        int Mn = 3e4 + 1, Mx = 0;
        FOR(i, 1, n) Mn = min(Mn, a[i][1]), Mx = max(Mx, a[i][1]);
        int ans = Mx - Mn;
        Mn = 3e4 + 1, Mx = 0;
        FOR(i, 1, n) Mn = min(Mn, a[i][2]), Mx = max(Mx, a[i][2]);
        ans = max(ans, Mx - Mn);
        return void(printf("%d\n", ans));
    }
    if(k == 3)
    {
        mxp = mnp = 1;
        FOR(i, 1, n)
        {
            if(a[i][2] > a[i][3]) Mx[i] = 2;
            else Mx[i] = 3;
            if(i > 1)
            {
                if(a[i][Mx[i]] > a[mxp][Mx[mxp]]) mxp = i;
                if(a[i][1] < a[mnp][1]) mnp = i;
            }
        }
        auto chk = [&](int mid) -> bool
        {
            int sum = 0;
            FOR(s, 2, 3) if(mnp != mxp || (mnp == mxp && Mx[mxp] == s))
            {
                FOR(i, 1, a[mxp][Mx[mxp]]) pos[i].clear();
                memset(cnt, 0, sizeof(cnt)); sum = 0;
                FOR(i, 1, n)
                {
                    if(i == mnp)
                    {
                        if(a[mxp][Mx[mxp]] - a[i][s] <= mid)
                            pos[a[i][5 - s]].emplace_back(i, 0);
                    }
                    else if(i == mxp)
                    {
                        if(s == Mx[i])
                        {
                            if(a[i][1] - a[mnp][1] <= mid)
                                pos[a[i][5 - s]].emplace_back(i, 0);
                        }
                        else
                        {
                            if(a[i][s] - a[mnp][1] <= mid)
                                pos[a[i][1]].emplace_back(i, 0);
                        }
                    }
                    else
                    {
                        if(s == 2)
                        {
                            if(a[i][1] - a[mnp][1] <= mid && a[mxp][Mx[mxp]] - a[i][2] <= mid)
                                pos[a[i][3]].emplace_back(i, 0);
                            if(a[i][2] - a[mnp][1] <= mid && a[mxp][Mx[mxp]] - a[i][3] <= mid)
                                pos[a[i][1]].emplace_back(i, 0);
                            if(a[i][3] - a[mnp][1] <= mid && a[mxp][Mx[mxp]] - a[i][1] <= mid)
                                pos[a[i][2]].emplace_back(i, 0);
                        }
                        else
                        {
                            if(a[i][1] - a[mnp][1] <= mid && a[mxp][Mx[mxp]] - a[i][3] <= mid)
                                pos[a[i][2]].emplace_back(i, 0);
                            if(a[i][2] - a[mnp][1] <= mid && a[mxp][Mx[mxp]] - a[i][1] <= mid)
                                pos[a[i][3]].emplace_back(i, 0);
                            if(a[i][3] - a[mnp][1] <= mid && a[mxp][Mx[mxp]] - a[i][2] <= mid)
                                pos[a[i][1]].emplace_back(i, 0);
                        }
                    }
                }
                FOR(i, 1, mid) for(auto [x, y] : pos[i])
                    if(++cnt[x] == 1) sum++;
                FOR(i, mid + 1, a[mxp][Mx[mxp]])
                {
                    for(auto [x, y] : pos[i - mid - 1])
                        if(--cnt[x] == 0) sum--;
                    for(auto [x, y] : pos[i])
                        if(++cnt[x] == 1) sum++;
                    if(sum == n) return 1;
                }
            }
            return 0;
        };
        int l = -1, r = a[mxp][Mx[mxp]];
        while(l + 1 < r)
        {
            int mid = (l + r) >> 1;
            if(chk(mid)) r = mid;
            else l = mid;
        }
        return void(printf("%d\n", r));
    }
    mnp = mxp = 1;
    FOR(i, 1, n)
    {
        if(a[i][2] == max(a[i][2], a[i][3], a[i][4])) Mx[i] = 2;
        if(a[i][3] == max(a[i][2], a[i][3], a[i][4])) Mx[i] = 3;
        if(a[i][4] == max(a[i][2], a[i][3], a[i][4])) Mx[i] = 4;
        if(i > 1)
        {
            if(a[i][1] < a[mnp][1]) mnp = i;
            if(a[i][Mx[i]] > a[mxp][Mx[mxp]]) mxp = i;
        }
    }
    auto chk = [&](int mid) -> bool
    {
        FOR(s, 2, 4) if(mxp != mnp || (mxp == mnp && s == Mx[mxp]))
        {
            build(1, 1, a[mxp][Mx[mxp]]);
            FOR(i, 1, a[mxp][Mx[mxp]]) pos[i].clear();
            FOR(i, 1, n) ss[i].clear();
            FOR(i, 1, n)
            {
                if(i == mnp)
                {
                    if(a[mxp][Mx[mxp]] - a[i][s] <= mid)
                    {
                        if(s == 2) pos[a[i][3]].emplace_back(i, a[i][4]);
                        else if(s == 3) pos[a[i][2]].emplace_back(i, a[i][4]);
                        else pos[a[i][2]].emplace_back(i, a[i][3]);
                    }
                }
                else if(i == mxp)
                {
                    int b[5]; FOR(j, 1, 4) b[j] = a[i][j];
                    rotate(b + 1, b + (((Mx[i] - s) % 4 + 4) % 4 + 1), b + 5);
                    if(b[1] - a[mnp][1] <= mid)
                    {
                        if(s == 2) pos[b[3]].emplace_back(i, b[4]);
                        else if(s == 3) pos[b[2]].emplace_back(i, b[4]);
                        else pos[b[2]].emplace_back(i, b[3]);
                    }
                }
                else
                {
                    FOR(j, 1, 4)
                    {
                        if(a[i][1] - a[mnp][1] <= mid && a[mxp][Mx[mxp]] - a[i][s] <= mid)
                        {
                            if(s == 2) pos[a[i][3]].emplace_back(i, a[i][4]);
                            else if(s == 3) pos[a[i][2]].emplace_back(i, a[i][4]);
                            else pos[a[i][2]].emplace_back(i, a[i][3]);
                        }
                        rotate(a[i] + 1, a[i] + 2, a[i] + 5);
                    }
                }
            }
            FOR(i, 1, mid) for(auto [x, y] : pos[i])
            {
                int L = max(y - mid, 1), R = y;
                auto it = ss[x].lower_bound(y);
                if(it != ss[x].begin())
                {
                    int Ly = *prev(it);
                    L = max(L, Ly + 1);
                }
                if(it != ss[x].end())
                {
                    int Ry = *it;
                    R = min(R, Ry - mid - 1);
                }
                if(L <= R) modify(1, 1, a[mxp][Mx[mxp]], L, R, 1);
                ss[x].insert(y);
            }
            FOR(i, mid + 1, a[mxp][Mx[mxp]])
            {
                for(auto [x, y] : pos[i - mid - 1])
                {
                    int L = max(y - mid, 1), R = y;
                    auto it = ss[x].lower_bound(y);
                    if(it != ss[x].begin())
                    {
                        int Ly = *prev(it);
                        L = max(L, Ly + 1);
                    }
                    if(it != ss[x].end() && next(it) != ss[x].end())
                    {
                        int Ry = *next(it);
                        R = min(R, Ry - mid - 1);
                    }
                    if(L <= R) modify(1, 1, a[mxp][Mx[mxp]], L, R, -1);
                    ss[x].erase(it);
                }
                for(auto [x, y] : pos[i])
                {
                    int L = max(y - mid, 1), R = y;
                    auto it = ss[x].lower_bound(y);
                    if(it != ss[x].begin())
                    {
                        int Ly = *prev(it);
                        L = max(L, Ly + 1);
                    }
                    if(it != ss[x].end())
                    {
                        int Ry = *it;
                        R = min(R, Ry - mid - 1);
                    }
                    if(L <= R) modify(1, 1, a[mxp][Mx[mxp]], L, R, 1);
                    ss[x].insert(y);
                }
                if(tr[1] == n) return 1;
            }
        }
        return 0;
    };
    int l = -1, r = a[mxp][Mx[mxp]];
    while(l + 1 < r)
    {
        int mid = (l + r) >> 1;
        if(chk(mid)) r = mid;
        else l = mid;
    }
    printf("%d\n", r);
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    int T = rd(); k = rd();
    while(T--) solve();
    return 0;
}
posted @ 2023-03-07 07:31  zuytong  阅读(125)  评论(0编辑  收藏  举报