Codeforces Round #776 (Div
阅读原文时间:2023年07月08日阅读:5

Codeforces Round #776 (Div. 3)

给定你数组a:1 2 3 … n,一共有n次操作,每次操作可以把\(a_i\)移到最左边,然后对\(i+1\)位以后不会产生影响,每次操作可以进行任意次,例如 :5 4 3 1 2 ,现在处于第三次操作,那么我只能在\(a_3\)位置上操作,操作一次后:

3 5 4 1 2,操作两次后:4 3 5 1 2,以此类推,现在给你操作完之后的数组a,让你计算出他在每个位置上的操作数分别是多少?

题解:思维+模拟

我们可以倒过来想,因为每一次操作不会对他后面的数造成影响,所以元素n现在的位置一定是最后一次操作操作若干次得到的,那么我们就得到了最后一次的操作数,根据这个操作数我们进行还原,还原得到的数组中n-1的位置一定是倒数第二次操作得到的,这样我们就得到了倒数第二次的操作数….以此类推

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int n, a[N], b[N];
int ans[N];

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    for (int i = n; i >= 1; --i)
    {
        int step = -1;
        if (i != n)
        {
            for (int j = 1; j <= n; ++j)
                a[j] = b[j];
        }
        for (int j = 1; j <= i; ++j)
            if (a[j] == i)
            {
                if (j != i)
                    step = j;
                else
                    step = 0;
                break;
            }
        if (step == -1)
        {
            cout << -1 << endl;
            return;
        }
        ans[i] = step;
        for (int j = 1; j <= i; ++j)
        {
            int pos = (j - step + i) % i;
            if (pos == 0)
                pos += i;
            b[pos] = a[j];
        }
        b[i] = i;
    }
    for (int i = 1; i <= n; ++i)
        cout << ans[i] << " ";
    cout << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

给你一张1-d长度的时间表,在这个时间表内安排了n场考试,你现在只能任意改变一场考试的时间,使得两场考试之间的休息时间u的最小值最大

题解:贪心+模拟

其实二分答案也能做,但是太难调了,直接放弃了,我们发现如果要使u变大,我们需要改变休息时间最短的两场考试\(a_i,a_{i-1}\)其中之一,那么假设我们改变\(a_i\)的时间,那么它可以插入的地方只有两个地方才有可能使得答案变大:

  1. 休息时间最长的两场考试中间,那么插入后他们之间的休息时间为:\((a_j-a_{j-1}-2)/2\)
  2. 插在最后一天\(d\)处,休息时间为:\(d-a[last]-1\),注意\(a[last]\)是会改变的

那么我们贪心在这两种插入中我们选择最大值,然后最大值再和改变后休息时间最短\(minn\)取\(min\)即可

那么我们已经考虑了改变\(a_i\)的情况,对于\(a_{i-1}\)只要\(i>1\)我们都可以按照上面的流程再走一遍,看一下u会不会更大

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int n, d;
int a[N];

int cal(int pos)
{
    int pre = 0;
    int minn = INF, maxx = -INF;
    for (int i = 1; i <= n; ++i)
    {
        if (i != pos)
        {
            minn = min(minn, a[i] - a[pre] - 1);
            maxx = max(maxx, a[i] - a[pre] - 1);
            pre = i;
        }
    }
    return min(minn, max((maxx - 1) / 2, d - a[pre] - 1));
}

void solve()
{
    cin >> n >> d;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    int minn = INF;
    int pos = -1;
    for (int i = 1; i <= n; ++i)
        if (a[i] - a[i - 1] - 1 < minn)
        {
            pos = i;
            minn = a[i] - a[i - 1] - 1;
        }
    int ans = -INF;
    ans = max(ans, cal(pos));
    if (pos > 1)
        ans = max(ans, cal(pos - 1));
    cout << ans << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

现在给你n个任务,每个任务都有截止时间,你需要将每个任务进程都完成100%,才算成功,又给定m种操作,每次操作给定\(e,t,p\),代表对于任务e,可以利用t时间将其进度增加p,每种操作只能用一次,求最后能否完成所有任务,如果能完成求出完成所有任务的最短用时,并将选择的操作以任意顺序输出

题解:01背包求方案数,感觉是一道好题目,下次记得复习

首先这是一个先明显的01背包求方案数的题目,因为存在截止时间,所以我们肯定要从截止时间短的任务开始做起,我们只需要对于每一个任务所对应的操作进行01背包,然后如果该任务完成的最短时间超过其给定的时间,那么说明后面的任务都完成不了了,如果能完成,那么剩余的时间我们可以分配给下一个任务,那么对于每次任务我们在dp时再记录方案即可

对于这道题,两个重点我们需要把握:

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 1e5 + 10, M = 4e5 + 10;

int n, m;
vector<array<int, 3>> v[N];
int a[N], f[N];
int dif[N];
vector<int> ans;

void solve()
{
    ans.clear();
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    for (int i = 1; i <= n; ++i)
        dif[i] = a[i] - a[i - 1];
    for (int i = 1; i <= n; ++i)
        v[i].clear();
    for (int i = 1, e, t, p; i <= m; ++i)
    {
        cin >> e >> t >> p;
        v[e].push_back({t, p, i});
    }
    for (int i = 1; i <= n; ++i)
    {
        vector<int> pre[101];
        f[0] = 0;
        for (int j = 1; j <= 105; ++j)
            f[j] = INF;
        for (auto &[t, p, id] : v[i])
        {
            for (int j = 100; j >= 0; --j)
            {
                if (f[j] > f[max(0ll, j - p)] + t)
                {
                    f[j] = f[max(0ll, j - p)] + t;
                    pre[j] = pre[max(0ll, j - p)];
                    pre[j].push_back(id);
                }
            }
        }
        if (f[100] > dif[i])
        {
            cout << -1 << endl;
            return;
        }
        dif[i + 1] += dif[i] - f[100];
        for (auto id : pre[100])
            ans.push_back(id);
    }
    cout << ans.size() << endl;
    for (auto id : ans)
        cout << id << " ";
    cout << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

给定一个无向图,没有自环和重边,给定起点st和终点ed,假设st到ed的最短距离为x,让你求出所有st到ed路径长度<=x+1的所有路径数(可以是简单路径,也可以不是简单路径)

题解:次短路和最短路求方案数 + 计数DP: 需要回顾复习

这是一道经典的最短路求方案数的问题,但同时我们只要再记录次短路的长度和方案数即可,我们可以在\(dijktra\)中对方案数进行记录和转移

现在解释一下\(dis[u][0/1]\):表示起点到u节点的最短距离(0)和次短距离(1)

同时我们在跑dij的时候将路径入队的同时需要分清楚这条路径是最短路还是次短路,我们可以定义结构体,在结构体中增加一个\(flag\)用来区分最短路和次短路

我们先来解释一下dp计数部分:

状态表示:\(f[u][0/1]\):代表从起点到u节点最短路径(0)的方案数,次短路(1)的方案数

状态属性:数量

状态转移:我们考虑四种情况

  1. \(dis[u][type]+w\)小于最短路\(dis[v][0]\),如果最短路已经被松弛过了,我们可以将其方案数和距离转移给次短距离,并将次短路入队,然后再将\(f[u][type]\)转移给最短路后入队;如果没有被松弛过,直接将\(f[u][type]\)转移给最短路后入队
  2. \(dis[u][type]+w\)等于最短路\(dis[v][0]\),该点最短路的方案数需要增加\(f[v][0] += f[u][type]\)
  3. \(dis[u][type]+w\)大于次短路\(dis[v][1]\),修改次短路后,对方案数进行转移\(f[v][1] = f[u][type]\),然后入队
  4. \(dis[u][type]+w\)等于次短路\(dis[v][1]\),该点次短路的方案需要增加\(f[v][1] += f[u][type]\)

那么对于答案输出部分:

如果\(f[ed][1]-f[ed][0]==1\),说明满足题目条件,答案为\(f[ed][0]+f[ed][1]\)

如果\(f[ed][1]-f[ed][0]!=1\), 不满足题目条件,答案为\(f[ed][0]\)

#include <bits/stdc++.h>
#define Zeoy std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#define debug(x) cerr << #x << '=' << x << endl
#define all(x) (x).begin(), (x).end()
#define rson id << 1 | 1
#define lson id << 1
#define int long long
#define mpk make_pair
#define endl '\n'
using namespace std;
typedef unsigned long long ULL;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-9;
const int N = 2e5 + 10, M = 4e5 + 10;

int n, m;
vector<pii> g[N];
int st, ed;
int f[N][2], dis[N][2], vis[N][2];
struct node
{
    int u, dis, flag;
    bool operator<(const node &t) const
    {
        return dis > t.dis;
    }
};

void dij(int st)
{
    for (int i = 1; i <= n; ++i)
    {
        dis[i][0] = dis[i][1] = inf;
        f[i][0] = f[i][1] = 0;
        vis[i][0] = vis[i][1] = 0;
    }
    priority_queue<node> q;
    q.push({st, 0, 0});
    dis[st][0] = 0;
    f[st][0] = 1;
    while (q.size())
    {
        node t = q.top();
        int u = t.u, type = t.flag;
        q.pop();
        if (vis[u][type])
            continue;
        vis[u][type] = 1;
        for (auto &[v, w] : g[u])
        {
            if (dis[v][0] > dis[u][type] + w)
            {
                if (dis[v][0] != inf)
                {
                    f[v][1] = f[v][0];
                    dis[v][1] = dis[v][0];
                    q.push({v, dis[v][1], 1});
                }
                f[v][0] = f[u][type];
                dis[v][0] = dis[u][type] + w;
                q.push({v, dis[v][0], 0});
            }
            else if (dis[v][0] == dis[u][type] + w)
                f[v][0] = (f[v][0] + f[u][type]) % mod;
            else if (dis[v][1] > dis[u][type] + w)
            {
                f[v][1] = f[u][type];
                dis[v][1] = dis[u][type] + w;
                q.push({v, dis[v][1], 1});
            }
            else if (dis[v][1] == dis[u][type] + w)
                f[v][1] = (f[v][1] + f[u][type]) % mod;
        }
    }
}

void solve()
{
    cin >> n >> m;
    cin >> st >> ed;
    for (int i = 1; i <= n; ++i)
        g[i].clear();
    for (int i = 1, u, v; i <= m; ++i)
    {
        cin >> u >> v;
        g[u].push_back({v, 1});
        g[v].push_back({u, 1});
    }
    dij(st);
    if (dis[ed][1] - dis[ed][0] == 1)
        cout << (f[ed][0] + f[ed][1]) % mod << endl;
    else
        cout << f[ed][0] << endl;
}
signed main(void)
{
    Zeoy;
    int T = 1;
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}