2.14 寒假练习赛Round 2

T1 NKOJ 3677 观光车

何老板带领n名游客来到一景区大门口,需要乘坐观光车游览景区。

景区提供两种观光车,一种是每辆车可以坐a名游客,包一辆车费用是p1块钱;另一种每辆车可以坐b名游客,包一辆车费用是p2块钱。

何老板想让这n名游客都坐上观光车,且每辆车都坐满。问何老板至少要花费多少钱?

思路:首先尽可能多用性价比高的车,然后再不断调整两种车的数量,让性价比高的车最多即可。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

ll gcd(ll a, ll b) 
{
    return b ? gcd(b, a % b) : a;
}

main()
{
    ll n, x, y, a, b;
    scanf("%lld%lld%lld%lld%lld", &n, &x, &a, &y, &b);
    if (n % gcd(a, b)) return puts("-1"), 0;
    ll k = gcd(a, b);
    if (x * b > y * a) swap(x, y), swap(a, b);
    a /= k, b /= k, n /= k;
    for (ll i = n / a; i; i--)
    {
        if ((n - a * i) % b == 0)
        {
            ll j = (n - a * i) / b;
            printf("%lld", x * i + y * j);
            return 0;
        }
    }
    puts("-1");
}

T2 NKOJ 3950 越狱

监狱有连续编号为1...N的N个房间,每个房间关押一个犯人,有M种宗教,每个犯人可能信仰其中一种。如果
相邻房间的犯人的宗教相同,就可能发生越狱,求有多少种状态可能发生越狱

思路:反向思考,已知所有的方案数,那么考虑所有相邻房间的犯人的宗教均不相同的方案数,二者相减即可。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const ll mod = 100003;

inline ll KSM(ll a, ll b)
{
    ll ans = 1;
    while (b)
    {
        if (b & 1) (ans *= a) %= mod;
        (a *= a) %= mod, b >>= 1;
    }
    return ans;
}

main()
{
    ll m, n;
    scanf("%lld%lld", &m, &n);
    ll x = KSM(m, n), y = m * KSM(m - 1, n - 1) % mod;
    printf("%lld", (x - y + 2 * mod) % mod);
}

T3 NKOJ 2044 奶牛政坛

建议自行看题。

思路:可以证明,n个点中任两点的距离的最大值,一定是这n个点中深度最大的那个点和其他点的距离的最大值(不知道讲清楚没有)求距离直接lca即可。

#include <bits/stdc++.h>

using namespace std;

struct node {
    int to, nxt;
}e[400005];
int tot, head[200005], fa[200005][30], dep[200005], a[200005], maxd[200005], ans[200005];
inline void add_e(int u, int v) {e[++tot].to = v; e[tot].nxt = head[u]; head[u] = tot;}

void dfs(int x)
{
    for (int i = head[x]; i; i = e[i].nxt)
    {
        int y = e[i].to;
        if (y != fa[x][0])
        {
            fa[y][0] = x;
            dep[y] = dep[x] + 1;
            dfs(y);
        }
    }
}

inline int lca(int x, int y)
{
    if (dep[x] < dep[y]) swap(x, y);
    int k = dep[x] - dep[y];
    for (int i = 0; i <= 24; i++)
    {
        if (k & (1 << i)) x = fa[x][i];
    }
    if (x == y) return x;
    for (int i = 24; ~i; i--)
    {
        if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
    }
    return fa[x][0];
}

int main()
{
    int n, k, rt;
    scanf("%d%d", &n, &k);
    for (int i = 1, j; i <= n; i++)
    {
        scanf("%d%d", &a[i], &j);
        if (!j) rt = i;
        add_e(i, j), add_e(j, i);
    }
    dfs(rt);
    for (int j = 1; j <= 24; j++)
      for (int i = 1; i <= n; i++)
        fa[i][j] = fa[fa[i][j - 1]][j - 1];
    for (int i = 1; i <= n; i++)
    {
        if (dep[maxd[a[i]]] < dep[i]) maxd[a[i]] = i;
    }
    for (int i = 1; i <= n; i++)
    {
        int dis = dep[i] + dep[maxd[a[i]]] - 2 * dep[lca(i, maxd[a[i]])];
        ans[a[i]] = max(ans[a[i]], dis);
    }
    for (int i = 1; i <= k; i++)
    {
        printf("%d\n", ans[i]);
    }
}

T4(订正) NKOJ 4914 下载讲义

信竞期末考试要到了,你想要进行全面的复习。你来到机房,打算从网盘下载这学期何老板发的所有讲义。
共有n份讲义(编号1到n)需要下载。机房一共有m台(编号1到m)电脑,每台电脑都可使用,但一开始所有电脑都处于关机状态。
你事先已经得知:
电脑的开机耗时可能不同,第i台电脑开机需要Si秒钟。
同一份讲义,用不同的电脑下载,耗时可能不同。用第i台电脑下载第j份讲义,需要花费Dij秒。

你需要计算,下载完所有讲义,最少需要多少秒钟(所有电脑耗时总和最少)。

思路:这个题确实棋差一着,其实可以设f[i][s]为用(不一定开每一台)前i台电脑,下载讲义情况为s所用时间的最小值,有方程f[i][s | 1 << k - 1] = min(f[i][s | 1 << k - 1], f[i][j] + a[i][k]),直接切。

#include <bits/stdc++.h>

using namespace std;

int a[105][20], f[105][(1 << 16) + 1], s[105];

int main()
{
    int m, n;
    scanf("%d%d", &m, &n);
    int tot = (1 << n) - 1;
    for (int i = 1; i <= m; i++)
    {
        scanf("%d", &s[i]);
        for (int j = 1; j <= n; j++)
        {
            scanf("%d", &a[i][j]);
        }
    }
    memset(f, 0x3f, sizeof f); 
    f[0][0] = 0;
    for (int i = 1; i <= m; i++)
    {
        for (int j = 0; j <= tot; j++)
        {
            f[i][j] = f[i - 1][j] + s[i];
        }
        for (int k = 1; k <= n; k++)
        {
            for (int j = 0; j <= tot; j++)
            {                
                if (!(j & (1 << k - 1))) 
                {
                    f[i][j | (1 << k - 1)] = min(f[i][j | (1 << k - 1)], f[i][j] + a[i][k]);
                }
            }
        }
        for (int j = 0; j <= tot; j++) f[i][j] = min(f[i - 1][j], f[i][j]);
    }
    printf("%d", f[m][tot]);
}

T5 NKOJ 5617 黑白球

何老板将2N个小球排成一排。其中有N个白球和N个黑球。
白球编号1到N,每个白球上都有一个数字,表示它的编号。
黑球编号1到N,每个黑球上都有一个数字,表示它的编号。
可以任意交换两个相邻小球,何老板想知道,最少几次交换就能满足下列要求:
对于任意的(i,j), 1 ≤ i < j ≤ N,编号i的白球一定在编号j的白球左侧
对于任意的(i,j), 1 ≤ i < j ≤ N,编号i的黑球一定在编号j的黑球左侧

简单的说,最少进行几次交换,就能使得编号小的白球一定出现在编号大的白球的左侧,编号小的黑球一定出现在编号大的黑球的左侧。

思路:dp,设f[i][j]为摆前i个白球,前j个黑球的最小代价,有方程

f[i][j] = min(f[i][j], f[i - 1][j] + costw[i - 1][j]), f[i][j] = min(f[i][j], f[i][j - 1] + costb[i][j - 1])

预处理出cost数组即可。

#include <bits/stdc++.h>

using namespace std;

int w[2005], b[2005], valw[2005][2005], valb[2005][2005], f[2005][2005];

int main()
{
    int n;
    char ch;
    scanf("%d", &n);
    for (int i = 1, x; i <= 2 * n; i++)
    {
        cin >> ch; scanf("%d", &x);
        if (ch == 'W') w[x] = i;
        else b[x] = i;
    }
    for (int i = 0; i < n; i++)
    {
        for (int j = 1; j <= i; j++)
        {
            valw[i][0] += (w[j] > w[i + 1]);
        }
        for (int j = 1; j <= n; j++)
        {
            valw[i][j] = valw[i][j - 1] + (b[j] > w[i + 1]); 
        }
    }
    for (int j = 0; j < n; j++)
    {
        for (int i = 1; i <= j; i++)
        {
            valb[0][j] += (b[i] > b[j + 1]);
        }
        for (int i = 1; i <= n; i++)
        {
            valb[i][j] = valb[i - 1][j] + (w[i] > b[j + 1]);
        }
    }
    memset(f, 0x3f, sizeof f);
    f[0][0] = 0;
    for (int i = 0; i <= n; i++)
    {
        for (int j = 0; j <= n; j++)
        {
            if (i) f[i][j] = min(f[i][j], f[i - 1][j] + valw[i - 1][j]);
            if (j) f[i][j] = min(f[i][j], f[i][j - 1] + valb[i][j - 1]);
        }
    }
    printf("%d\n", f[n][n]);
}

 

 
posted @ 2021-02-22 16:44  Chasing-Dreams  阅读(109)  评论(0)    收藏  举报