首页 > 技术文章 > 1.16 高一周赛总结

Chasing-Dreams-Z 2021-01-16 16:03 原文

wtclwtcl!! T3的简单式子我推了半个小时才推正确额额额,导致没时间做T2 。

T1:NKOJ4312 飞行管制

题目大意:给定若干架飞机的原定起飞时间和延误单位时间的代价,先要求在第k + 1分钟才能开始发机,求最小代价。

题解:应该是一道老题了,我感觉我见过的。反正话不多说,这个题直接维护一个最小代价的小根堆,然后每次直接取出堆顶计算代价即可。

#include<bits/stdc++.h>

using namespace std;

#define ll long long

const ll maxn = 600005; 

struct node{
    ll m, id;
    bool operator> (node x) const { 
        return m == x.m ? id < x.id : m < x.m;
    }
}a[maxn];          
priority_queue<node, vector <node>, greater <node> > q;

ll ans[maxn];

main()
{
    ll n, k;
    scanf("%lld%lld", &n, &k);
    for (ll i = 1; i <= n; i++)
    {
        scanf("%lld", &a[i].m);
        a[i].id = i;
    }
    for(ll i = 1; i <= k + 1; i++)
    {
        q.push(a[i]);
    }
    ll hd = k + 1, tl = k + 2, sum = 0;
    ans[q.top().id] = hd; 
    sum += (hd - q.top().id) * q.top().m; 
    q.pop();
    for (ll i = 2; i <= n; i++)
    { 
        if (tl <= n)
        { 
            q.push(a[tl]);
        }
        if (tl <= n)
        { 
            tl++;
        }hd++;
        sum += (hd - q.top().id) * q.top().m;
        ans[q.top().id] = hd;
        q.pop();
    }
    printf("%lld\n", sum);
}

T2(订正) NKOJ7842 疫情防控

 

 这个题连向神都没有做出来。。我以为它很难。。。

题目大意:有n座城市和m条双向道连接各个城市,先要求将双行道改为单行道,使任两座都只有一种走法,判断是否有解。

题解:我一拿到这个题就傻了,还想着去跑dfs或者搞一搞强联通分量之类的东西。。

然鹅事实上这个题只需要判断1至n是否有环,有则有解,否则无解,于是直接用并查集维护点是否已经联通即可。

#include <bits/stdc++.h>

using namespace std;

bool mark[100005];
int fa[100005];

int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) fa[i] = i;
    while (m--)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        int fx = find(x), fy = find(y);
        if (fx != fy) 
        {
            fa[fx] = fy, mark[fy] = (mark[fx] | mark[fy]);
        }
        else mark[fx] = 1;
    }
    for (int i = 1; i <= n; i++) if (!mark[find(i)]) return puts("NO"), 0;
    puts("YES");
}

T3 NKOJ7836 玩玩异或

题目大意:给定一串数,要求支持两个操作:单点修改和区间查询所有子区间异或和的异或和。

题解:一开始看到这个题傻了,后来看到丁神(%%%!!!)爆切此题,我再仔细分析此题,发现这就是个大水题。

单点修改很简单,用a[i]记录i位置当前的数值,众所周知一个数异或它本身等于0,我们利用这个性质,可以在修改时先再异或一次a[i]然后直接修改a[i],再将修改后的值异或一次。而区间查询我推了好久,发现了两个规律:

1. 若区间左端点位置和右端点位置奇偶性不同则答案为0.

2. 若区间左右端点均为奇数位,则答案等于区间内所有奇数位上的数的异或和,若都为偶数则答案等于区间内所有偶数位上的数的异或和。

又因为异或具有前缀性质,于是想到用树状数组分别维护奇数位和偶数位异或和,查询时类似前缀一样异或即可(这大概是我写过的最详细的题解了)。

#include <bits/stdc++.h>

using namespace std;

int c[200005][2], a[200005], n, q;

void modify(int x, int p, int k) 
{
    for (int i = x; i <= n; i += (i & -i)) c[i][k] ^= p;
}

int query(int x, int k)
{
    int ans = 0;
    for (int i = x; i; i -= (i & -i)) ans ^= c[i][k];
    return ans;
}

int main()
{
    scanf("%d%d", &n, &q);
    for (int i = 1, x; i <= n; i++) scanf("%d", &a[i]), modify(i + 1 >> 1, a[i], i & 1);
    for (int i = 1, type, l, r; i <= q; i++)
    {
        scanf("%d%d%d", &type, &l, &r);
        if (type == 1)
        {
            modify(l + 1 >> 1, a[l] ^ r, l & 1);
            a[l] = r;
        }
        else
        {
            if (r - l & 1)
            {
                puts("0");
            }
            else
            {
                if (l & 1) printf("%d\n", query(r + 1 >> 1, 1) ^ query(l - 1 >> 1, 1));
                else printf("%d\n", query(r >> 1, 0) ^ query((l >> 1) - 1, 0));
            }
        }
    }
}

T4 NKOJ3751 扫雷游戏

题面:有一款有趣的手机游戏。棋盘上有n颗地雷,玩家需要至少扫掉其中的k颗雷。
每一步,玩家可以用手指在手机屏幕上划一条直线,该直线经过的地雷都会被扫除掉。
问,最少需要划几次就能扫除k颗以上的地雷?

题解:这个题状压 + 记忆化搜索,先通过斜率判断是否三点一线,用状压记录下共线的情况,然后直接有没扫的点直接逐个搜索将它扫掉即可。

ps:这个题我因为判断共线时将一个减号写成了乘号直接调了半个小时(小声哔哔)。

#include <bits/stdc++.h>

using namespace std;

struct node {
    int x, y;
}a[(1 << 16) + 5];
int Map[20][20], f[(1 << 16) + 5], n, m;

inline bool check(int i, int j, int k) {return (a[i].x - a[j].x) * (a[i].y - a[k].y) == (a[i].y - a[j].y) * (a[i].x - a[k].x);}

int dfs(int s)
{
//    printf("%d\n", s);
    if (f[s] != 0x3f3f3f3f) return f[s];
    int sum = 0;
    for (int i = 1; i <= n; i++) if ((1 << i - 1) & s) sum++;
    if (n - m >= sum) return f[s] = 0;
    if (sum == 1) return f[s] = 1;
    for (int i = 1; i <= n; i++)
    {
        if ((1 << i - 1) & s)
        {
            for (int j = i + 1; j <= n; j++)
            {
                if ((1 << j - 1) & s) f[s] = min(f[s], dfs(s & (~Map[i][j])) + 1);
            }
        }
    }
    return f[s];
}

int main()
{
    for (int pp = 2; pp; pp--)
    {
        int tot;
        scanf("%d%d", &n, &m);
        tot = (1 << n) - 1;
        for (int i = 1; i <= n; i++)
        {
            scanf("%d%d", &a[i].x, &a[i].y);
        }
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                if (i != j)
                {
                    for (int k = 1; k <= n; k++)
                    {
                        if (check(i, j, k)) Map[i][j] |= (1 << k - 1);
                    }
                }
            }
        }
        memset(f, 0x3f, sizeof(f));
//        for(int s = 0; s <= tot; s ++)f[s] = 0x3f3f3f3f;
        printf("%d\n", dfs(tot));
        memset(Map, 0, sizeof(Map));
        memset(a, 0, sizeof(a));
    }
}

 

推荐阅读