补题集合1(num.15)

VJ

A - Alehouse

题意:
        酒馆有 n 个人,每个人都有一个固定的进入和离开的时间,然后你会去酒馆交朋友,只要遇到就会成为朋友,即使是在门口相遇,然后你最多只会待 m 个时间,求出最多能交多少朋友。

思路:

        模拟,将每个人进入和离开的时间分开存储后进行排序,然后遍历模拟一遍,记录过程中的最大人数。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+5;
typedef pair<int, int>PII;
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
int T, n, m;
int cnt = 0;
struct node
{
    int x, y;
}tr[N];
bool cmp(node aa, node bb)
{
    return aa.x < bb.x;
}
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    for(int i = 0; i < n; i ++)
    {
        int x, y;
        cin >> x >> y;
        tr[cnt ++] = {x, 1};
        tr[cnt ++] = {y, -1};
    }
    sort(tr, tr + cnt, cmp);
    int num = 0, ans = 0;
    for(int i = 0, j = 0; i < cnt; i ++)
    {
        while(j < cnt && tr[j].x <= tr[i].x + m)
        {
            if(tr[j].y == 1) num ++;
            j ++;
        }
        ans = max(ans, num);
        if(tr[i].y == -1) num --;
    }
    cout << ans << '\n';
    return 0;
}

Deranging Hat

        对于一个排好序的字符串,经过多次交换两个字母,能排列成给定字符串。但是当只有 ai 大于 bi 时才允许交换,如果反向思维去想的话,我们可以把给定的字符串,通过多次交换两个字母的位置来变成排好序的字符串,再把交换的顺序逆向输出即可。时间复杂度允许 n^2,但是要注意的是,每次我们都要去交换最小的那个字母,才能保证反过来之后,它是 ai 大于 bi,满足题目要求。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
const int N = 2e5+5;
typedef pair<int, int>PII;
int T, n, m;
vector<PII>v;

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    string s;
    cin >> s;
    int t = s.size();
    for(int i = 0; i < t; i ++)
    {
        char c = s[i];
        int x;
        for(int j = i + 1; j < t; j ++)
        {
            if(s[j] < c)
            {
                x = j;
                c = s[j];
            }
        }
        if(c == s[i]) continue;
        swap(s[i], s[x]);
        v.push_back({i, x});
    }
    for(int i = v.size() - 1; i >= 0; i --)
    {
        cout << v[i].second+1 << " " << v[i].first+1 << '\n';
    }
}

Flipping Coins

        这个题,打训练赛的时候没读懂题意。它是要保证翻转 K 次的,即使已经全部都是正面了,也要继续翻转直到 K 次。

        可以考虑用 dp 来进行状态转移,最后加和。

        f[ i ][ j ] 用来表示 i 次翻转后,有 j 个硬币朝上,那么 f[ i ][ j ] += f[ i - 1 ][ j ] * 0.5 并且 f[ i ][ j ] += f[ i - 1 ][ j - 1 ] * 0.5。但是对于 j == n 时,由于不可能有 n + 1 个硬币朝上,所以转移时 f[ i ][ n - 1 ] += f[ i - 1][ n ] * 0.5。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
const int N = 2e3+5;
typedef pair<int, int>PII;
int T, n, m;
double f[N][N];

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    f[0][0] = 1;
    for(int i = 1; i <= m; i ++)
    {
        for(int j = 0; j <= n; j ++)
        {
            // 概率是累加的而不是由上一级直接赋值,是因为由多个状态可以走到当前状态
            // 比如f[3][2]可以由f[2][2]和f[2][1]两种状态转移而来
            f[i][j] += f[i-1][j] * 0.5;
            if(j > 0) f[i][j] += f[i-1][j - 1] * 0.5;
            if(j == n) f[i][n - 1] += f[i - 1][n] * 0.5;
        }
    }
    double ans = 0;
    for(int i = 1; i <= n; i ++) ans += f[m][i] * i;
    printf("%.10lf\n", ans);
    return 0;
}

牛客

牛客练习赛121

氧气少年的 LCM

        给定两个数,然后用两种方法构建出这两个数的最小共倍数。当时确实没想出来,点一下就明白了,lcm 一定是 gcd 的 n 倍。所以可以先添加 gcd,通过 gcd 构建 lcm。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+5;
typedef pair<int, int>PII;
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
int T, n, m;
int a[N];
PII b[N];
vector<int>v;

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> T;
    while(T --)
    {
        v.clear();
        int cnt = 0;
        int x, y;
        cin >> x >> y;
        
        int lc = lcm(x, y), gc = gcd(x, y);
        int z = gc;
        a[++ cnt] = 1;
        b[cnt] = {x, y};
        a[++ cnt] = 1;
        b[cnt] = {x, y};
        v.push_back(gc);
        while(1)
        {
            if(gc * 2 > lc) break;
            a[++cnt] = 2;
            b[cnt] = {gc, gc};
            a[++cnt] = 2;
            b[cnt] = {gc, gc};
           
            gc *= 2;
            v.push_back(gc);
        }
        v.push_back(Max);
        while(gc < lc)
        {
            int c = lc - gc;
            int tt = upper_bound(v.begin(), v.end(), c) - v.begin();
            tt --;
            a[++cnt] = 2;
            b[cnt] = {gc, v[tt]};
            gc += v[tt];
        }
        cout << cnt << '\n';
        for(int i = 1; i <= cnt; i ++)
        {
            cout << a[i] << " " << b[i].first << " " << b[i].second << '\n';
        }
    }
    return 0;
}

牛客周赛 Round 31

小红的子集取反

        这个题比较难想,其实这个题可以用背包轻松写出来,先得出整个数组的和 sum,然后判断 abs(sum)是不是奇数,如果是奇数一定 -1,否则,去做容量为 sum/2 的 01 背包,判断是否有值即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+5;
typedef pair<int, int>PII;
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
int T, n, m;
int a[N], b[N], f[N];

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n;
    int sum = 0;
    for(int i = 1; i <= n; i ++)
    {
        cin >> a[i];
        sum += a[i];
    }
    int t = abs(sum);
    if(t % 2 == 1)
    {
        cout << "-1\n";
        return 0;
    }
    int cnt = 0;
    if(sum < 0)
    {
        for(int i = 1; i <= n; i ++)
        {
           if(a[i] < 0) b[++cnt] = - a[i];
        }
    }
    else
    {
        for(int i = 1; i <= n; i ++)
        {
            if(a[i] > 0) b[++cnt] = a[i];
        }
    }
    memset(f, 0x3f, sizeof f);
    f[0] = 0;
    t /= 2;
    for(int i = 1; i <= cnt; i ++)
    {
        for(int j = t; j >= b[i]; j --)
        {
            f[j] = min(f[j], f[j-b[i]] + 1);
        }
    }
    if(f[t] == Max) cout << "-1\n";
    else cout << f[t] << '\n';
}

小红的连续段

        将 n 个相同的物品,分成 m 份,{1,2} 和 {2,1}是两种分法,{1,1} 和 {1,1}是一种分法,那么将有 C(n - 1,m - 1)种分法。

        

思路来源:牛客周赛 Round 31 解题报告 | 珂学家 | 设计 + 组合_牛客博客 (nowcoder.net)

代码如下: 

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
const int N = 2e5+5;
typedef pair<int, int>PII;
int T, n, m;
const int mod = 1e9 + 7;
int fact[N], infact[N];

int qpow(int a, int b, int mod)
{
    int res = 1;
    while (b)
    {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}
void init()
{
    fact[0] = infact[0] = 1;
    for (int i = 1; i < N; ++ i) 
    {
        fact[i] = fact[i - 1] * i % mod;
        infact[i] = qpow(fact[i], mod - 2, mod);
    }
}

int ask(int a, int b)
{
    if(a < b) return 0;
    return fact[a] * infact[b] % mod * infact[a - b] % mod;
}

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    init();
    int x, y;
    cin >> x >> y;
    for(int i = 1; i <= (x + y); i ++)
    {
        int t1 = i / 2, t2 = i + 1 >> 1;
        int tt = ask(x-1, t1-1) * ask(y-1, t2-1) % mod;
        int x1 = i + 1 >> 1, x2 = i / 2;
        int tz = ask(x-1, x1-1) * ask(y-1, x2-1) % mod;
        cout << (tt+tz)%mod << '\n';
    }
}

2024牛客寒假算法基础集训营1

本题又主要考察了贪心

        题意挺简单,第一眼看过去就是暴搜,但是一直都不太会写递归,dfs,暴搜这种,所以写的很烂,然后开了很多没用的变量和 bool 数组并且跑了很多无用的循环,导致 TLE 了。一直卡在这个题也没有去做其他题,导致整场除了这个题之外就wa了一发依然罚时很高。后来做崩了,去做其他几个签到题却很快就做出来了,以后不能死钻牛角尖才是。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+5;
typedef pair<int, int>PII;
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
int T, n, m;
int a[N];
struct node
{
    int x, y;
}tr[N];
int ans;
void dfs(int u)
{
    if(u >= m)
    {
        int res = 1;
        for(int i = 2; i <= n; i ++)
        {
            if(a[i] > a[1]) res ++;
        }
        ans = min(ans, res);
        return;
    }
    a[tr[u].x] += 3;
    dfs(u + 1);
    a[tr[u].x] -= 3;
    a[tr[u].x] += 1;
    a[tr[u].y] += 1;
    dfs(u + 1);
    a[tr[u].x] -= 1;
    a[tr[u].y] -= 1;
    a[tr[u].y] += 3;
    dfs(u + 1);
    a[tr[u].y] -= 3;
}
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> T;
    while(T --)
    {
        ans = Max;
        cin >> n >> m;
        for(int i = 1; i <= n; i ++) cin >> a[i];
        for(int i = 0; i < m; i ++)
        {
            cin >> tr[i].x >> tr[i].y;
        }
        dfs(0);
        cout << ans << '\n';
    }
}

It's bertrand paradox. Again!

        一个关于概率的题,题目中有两种生成圆的方法,抓住这两种方法的区别后判断一些有区分度的生成数量即可。

        例如:第一种生成圆的方法生成的坐标是均匀分布的,但是第二种方法就不均匀分布。所以可以统计 x 和 y 坐标绝对值都在 70 以内的数量,然后与 141 * 141 / (199 * 199) * n 进行比较即可(141 是 [-70, 70] 有 141 个点,199 是 [-99, 99] 有199个点),在误差范围内就是第一种方法生成的圆,否则就是第二种方法生成的圆。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+5;
typedef pair<int, int>PII;
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
int T, n, m;

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n;
    int a1 = 0, b1 = 0;
    for(int i = 0; i < n; i ++)
    {
        int x, y, r;
        cin >> x >> y >> r;
        if(abs(x) <= 70 && abs(y) <= 70) a1 ++;
    }
    double t1 = 141 * 141.0 / 199 / 199 * n;
    if(abs(t1 - a1) <= 100) cout << "bit-noob\n";
    else cout << "buaa-noob\n";
    return 0;
}

2024牛客寒假算法基础集训营2 

Tokitsukaze and Slash Draw

        打比赛的感觉一眼完全背包,但是容量大,复杂度太高,一直在想怎么去优化。后来想到了取余操作,不断的分析和取余过程中,完全背包被我写成了 01 背包,后来又通过卡数据,给过掉了。这个取余操作,是对 n 取余,因为移动 n 个位置等于没有移动。 

        下面是出题人写的 dp 思路, 我写的代码限制了一个比较大的操作次数来卡数据,看样子并不是数据水过了,而是我代码跑的次数满足了最大的操作次数。

        先贴一个自己的代码,正解思路以后再看吧:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 3e7+5;
typedef pair<int, int>PII;
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
int T, n, m, k;
int v[N], w[N], id[N], f[N];
bool st[N];
int cnt;
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> T;
    while(T --)
    {
        cin >> n >> m >> k;
        for(int i = 0; i < n; i ++) st[i] = 0;
        int t = n - k;
        int cnt = 0;
        for(int i = 1; i <= m; i ++)
        {
            int x, y;
            cin >> x >> y;
            for(int j = 1; j <= n; j ++)
            {
                int tt = x*j%n;
                if(!st[tt])
                {
                    st[tt] = true;
                    v[++cnt] = tt;
                    w[cnt] = y*j;
                    id[tt] = cnt;
                }
                else w[id[tt]] = min(w[id[tt]], y*j);
            }
        }
        if(k == n)
        {
            cout << 0 << '\n';
            continue;
        }
        int mx = min(100000000/cnt, n*(n+1)/2);
        for(int i = 0; i <= mx; i ++) f[i] = Max;
        f[0] = 0;
        for(int i = 1; i <= cnt; i ++)
        {
            for(int j = mx; j >= v[i]; j --)
            {
                f[j] = min(f[j], f[j-v[i]] + w[i]);
            }
        }
        int ans = Max;
        for(int i = t; i <= mx; i += n)
        {
            ans = min(ans, f[i]);
        }
        if(ans == Max) cout << "-1\n";
        else cout << ans << '\n';
    }
    return 0;
}

        做法二:同余最短路(恍然大悟)。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e3+5;
typedef pair<int, int>PII;
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
int T, n, m, k;
int dis[N], d[N], w[N];
bool st[N];
priority_queue<PII, vector<PII>, greater<PII>>q;
void dijkstra()
{
    memset(dis, 0x3f, sizeof dis);
    dis[0] = 0;
    memset(st, 0, sizeof st);
    q.push({0, 0});
    while(q.size())
    {
        int t = q.top().second;
        q.pop();
        if(st[t]) continue;
        st[t] = true;
        for(int i = 0; i < m; i ++)
        {
            int z = (d[i] + t) % n;
            if(dis[z] > dis[t] + w[i])
            {
                dis[z] = dis[t] + w[i];
                q.push({dis[z], z});
            }
        }
    }
}
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> T;
    while(T --)
    {
        cin >> n >> m >> k;
        for(int i = 0; i < m; i ++)
        {
            int x, y;
            cin >> d[i] >> w[i];
        }

        dijkstra();

        if(dis[n-k] == Max) cout << "-1\n";
        else cout << dis[n-k] << '\n';
    }
    return 0;
} 

Tokitsukaze and Password (easy)

        打比赛的时候,看到是输出对 1e9+7 取模后的结果。感觉是 dfs 剪枝优化,而且会推出来什么式子之类的来减少算法复杂度,一直想不到怎么写。结果是为了让 easy 和 hard 题目尽量一致就没有删除这句话。 

        这个题,暴力枚举即可,dfs 的做法以后再补。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+5;
typedef pair<int, int>PII;
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
int T, n, m, y;
string s;
int check(int x)
{
    if(x == 0) return 1;
    else
    {
        int cnt = 0;
        while(x)
        {
            cnt ++;
            x /= 10;
        }
        return cnt;
    }
}
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> T;
    while(T --)
    {
        set<int>ans;
        cin >> n >> s >> y;
        for(int a = 0; a < 10; a ++)
            for(int b = 0; b < 10; b ++)
                for(int c = 0; c < 10; c ++)
                    for(int d = 0; d < 10; d ++)
                    {
                        set<int>v = {a, b, c, d};
                        if(v.size() < 4) continue;
                        for(int _ = 0; _ < 10; _ ++)
                        {
                            int res = 0;
                            for(auto x : s)
                            {
                                if(x == 'a') res = res * 10 + a;
                                else if(x == 'b') res = res * 10 + b;
                                else if(x == 'c') res = res * 10 + c;
                                else if(x == 'd') res = res * 10 + d;
                                else if(x == '_') res = res * 10 + _;
                                else res = res * 10 + (x ^ 48);
                            }
                            if(check(res) == n && res % 8 == 0 && res <= y) ans.insert(res);
                        }
                    }
        cout << ans.size() << '\n';
    }
    return 0;
}

2024牛客寒假算法基础集训营5

soyorin的数组操作(easy)

        打比赛的时候没有想明白从后向前遍历的思路,一直在考虑从前向后遍历。但是一直 wa,直到最后也没想到哪里错了,现在来看,突然就反应过来了。我在从前向后一点点遍历的时候,对于每个 i 都分奇偶判断,并且记录了一个能加的最大次数 cnt,a[i] + cnt * i <= a[i + 1] 是一种情况,会让 a[i] = a[i] + { ( a[i - 1] - a[i] ) / i + ( ( a[i - 1] - a[i] ) % i != 0 ) } * i;反之的另外一种情况则是 cnt 小,增大cnt,然后再同上一种情况加上,每次都会比较。其实,最大的问题在于,假设我在 i = 2 时增加了2 * i,又在 i = 4 时增加了 3 * i,那么在 i = 2 时就不可能只增加了 2 * i,至少也是 3 * i,也就是说后面的增加次数一定小于等于前面位置的增加次数,这里欠考虑了。

        从后倒着遍历会简单很多,首先我们已经把 n 是偶数的情况都判掉了,都是 YES。所以从倒数第二个数开始,把数加满,令 a[i] 变成不大于 a[i+1] 的最大数,然后是倒数第四个数...... 每次判一判就可以了。

赛时错误代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5;
typedef pair<int, int> PII;
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
int T, n, m;
int a[N];

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> T;
    while (T--)
    {
        cin >> n;
        for (int i = 1; i <= n; i++)
        {
            cin >> a[i];
        }

        if (n % 2 == 0)
        {
            cout << "YES\n";
            continue;
        }

        int mx = a[n], f = 0, cnt = 0;
        for (int i = 1; i < n; i++)
        {
            if (a[i] > a[i + 1])
            {
                if (i == n - 1)
                {
                    // cout << 1;
                    f = 1;
                    break;
                }
                if (a[i + 1] + cnt * (i + 1) < a[i])
                {
                    if ((i + 1) % 2 == 0)
                    {
                        a[i + 1] = a[i + 1] + cnt * (i + 1);
                        int tt = a[i] - a[i + 1];
                        cnt += tt;
                        if (mx - tt * (i + 1) < a[i + 1])
                        {
                            // cout << 2;
                            f = 1;
                            break;
                        }
                        a[i + 1] = a[i + 1] + tt * (i + 1);
                    }
                    else
                    {
                        a[i + 1] = a[i + 1] + cnt * (i + 1);
                        if (mx - cnt * (i + 2) < a[i + 2])
                        {
                            // cout << 3;
                            f = 1;
                            break;
                        }
                        a[i + 2] = a[i + 2] + cnt * (i + 2);
                        int tt = a[i] - a[i + 1];
                        cnt += tt;
                        if (mx - tt * (i + 1) < a[i + 1] || mx - tt * (i + 2) < a[i + 2])
                        {
                            // cout << 4;
                            f = 1;
                            break;
                        }
                        a[i + 1] = a[i + 1] + tt * (i + 1);
                        a[i + 2] = a[i + 2] + tt * (i + 2);
                    }
                }
                else
                {
                    int tt = a[i] - a[i + 1];
                    int t1 = tt / (i + 1), t2 = tt % (i + 1);
                    if (t2 != 0)
                        t1++;
                    if ((i + 1) % 2 == 0)
                    {
                        if (mx - t1 * (i + 1) < a[i + 1])
                        {
                            // cout << 5;
                            f = 1;
                            break;
                        }
                        a[i + 1] = a[i + 1] + t1 * (i + 1);
                    }
                    else
                    {
                        if (mx - t1 * (i + 1) < a[i + 1] || mx - t1 * (i + 2) < a[i + 2])
                        {
                            // cout << 6;
                            f = 1;
                            break;
                        }
                        a[i + 1] = a[i + 1] + t1 * (i + 1);
                        a[i + 2] = a[i + 2] + t1 * (i + 2);
                    }
                }
            }
        }
        // for(int i = 1; i <= n; i ++) cout << a[i] << " ";
        if (f == 1)
            cout << "NO\n";
        else
            cout << "YES\n";
    }
    return 0;
}

ac代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+5;
typedef pair<int, int>PII;
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
int T, n, m;
int a[N];

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> T;
    while(T --)
    {
        cin >> n;
        for(int i = 1; i <= n; i ++)
        {
            cin >> a[i];
        }
        if(n % 2 == 0)
        {
            cout << "YES\n";
            continue;
        }
        int f = 0, cnt = 0;
        for(int i = n - 1; i >= 1; i -= 2)
        {
            a[i] = a[i] + cnt * i;
            if(a[i] <= a[i+1])
            {
                int t = (a[i+1] - a[i]) / i;
                cnt += t;
                a[i-1] = a[i-1] + cnt * (i-1);
                a[i] = a[i] + t * i;
            }
            else
            {
                f = 1;
                break;
            }
        }
        for(int i = 1; i < n; i ++)
        {
            if(a[i] > a[i + 1])
            {
                f = 1;
                break;
            }
        }
        if(f == 1) cout << "NO\n";
        else cout << "YES\n";
    }
    return 0;
}

soyorin的数组操作(hard)

        判断可不可行,然后可行的话取 max(a[i] - a[i-1])即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+5;
typedef pair<int, int>PII;
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
int T, n, m;
int a[N];

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> T;
    while(T --)
    {
        cin >> n;
        for(int i = 1; i <= n; i ++)
        {
            cin >> a[i];
        }
        int mx = 0;
        for(int i = 1; i < n; i ++)
        {
            mx = max(mx, a[i] - a[i+1]);
        }
        if(n % 2 == 0)
        {
            cout << mx << "\n";
            continue;
        }
        int f = 0, cnt = 0;
        for(int i = n - 1; i >= 1; i -= 2)
        {
            a[i] = a[i] + cnt * i;
            if(a[i] <= a[i+1])
            {
                int t = (a[i+1] - a[i]) / i;
                cnt += t;
                a[i-1] = a[i-1] + cnt * (i-1);
                a[i] = a[i] + t * i;
            }
            else
            {
                f = 1;
                break;
            }
        }
        for(int i = 1; i < n; i ++)
        {
            if(a[i] > a[i + 1])
            {
                f = 1;
                break;
            }
        }
        if(f == 1) cout << "-1\n";
        else cout << mx << "\n";
    }
    return 0;
}

CF

Codeforces Round 932 (Div. 2)

C. Messenger in MAC

        对于一组 pi 的 bi 值,让其绝对值之和最小,很容易就能想到排序,但是如果仔细看的话就会发现,它们的绝对值之和其实就是 bmax -  bmin。而且顺序并不会影响 ai 的和大小。在按照 bi 的值 sort 之后,对于每个 i 开始向后遍历元素,计算总和与 L 比较,如果大于 L,把 ai 最大的元素删除。用 multiset 来维护这个序列。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
typedef pair<int, int>PII;
int T, n, m;
PII p[N];

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> T;
    while(T --)
    {
        cin >> n >> m;
        for(int i = 0; i < n; i ++)
        {
            cin >> p[i].second >> p[i].first;
        }
        sort(p, p + n);
        int ans = 0;
        for(int i = 0; i < n; i ++)
        {
            multiset<int>s;
            int res = 0;
            for(int j = i; j < n; j ++)
            {
                s.insert(p[j].second);
                res += p[j].second;
                while(s.size() && p[j].first - p[i].first + res > m)
                {
                    int it = *s.rbegin();
                    res -= it;
                    s.extract(it);
                }
                ans = max(ans, (int) s.size());
            }
        }
        cout << ans << '\n';
    }
}

        还有一种dp的做法,记 f[ i ][ j ] 是前 i 个物品取 j 个的最小价值。那么 f[ i ][ j ] = min(f[ k ][j-1] + a[ i ] + b[ i ] - b[ k ]),由于 a[ i ] 和 b[ i ] 是确定的,所以 f[ i ][ j ] = min(f[ k ][ j - 1] - b[ k ]) + a[ i ] + b[ i ]。

        由于 k 值不确定,所以复杂度是 n^3 的,但是在更新时,令 g[ j ] = min(f[ k ][ j ] - b[ k ]) 就可以优化成 n^2 的复杂度。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
const int N = 2e3+5;
typedef pair<int, int>PII;
int T, n, m;
PII p[N];
int f[N][N], g[N];

bool cmp(PII aa, PII bb)
{
    return aa.second < bb.second;
}
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> T;
    while(T --)
    {
        cin >> n >> m;
        for(int i = 1; i <= n; i ++)
        {
            cin >> p[i].first >> p[i].second;
        }
        sort(p+1, p+n+1, cmp);
        for(int i = 1; i <= n; i ++)
        {
            for(int j = 1; j <= n; j ++)
            {
                f[i][j] = Max;
                g[j] = Max;
            }
        }

        f[0][0] = 0, g[0] = 0;

        for(int i = 1; i <= n; i ++)
        {
            for(int j = i; j >= 2; j --)
            {
                f[i][j] = g[j-1] + p[i].first + p[i].second;
                g[j] = min(g[j], f[i][j] - p[i].second);
            }
            f[i][1] = p[i].first;   // 当只有一个值时,它的价值就是ai
            g[1] = min(g[1], f[i][1] - p[i].second);
        }
        for(int i = 1; i <= n; i ++) g[i] = Max;
        for(int i = 1; i <= n; i ++)
        {
            for(int j = 1; j <= i; j ++)
            {
                g[j] = min(g[j], f[i][j]);
            }
        }
        int ans = n;
        for(int i = 0; i <= n; i ++)
        {
            if(g[i] > m)
            {
                ans = i - 1;
                break;
            }
        }
        cout << ans << '\n';
    }
    return 0;
}

D. Exam in MAC

        容斥,首先求出总的,然后减去符合 x + y 的,减去符合 y - x 的,最后再加上两者都符合的即可。

        对于两者都符合的情况:x + y = ai 且 y - x = aj,那么两式相加得到 2y = ai + aj,当 ai + aj 为偶数时可以得到唯一解,所以都符合的数量就是 ai 与 aj 同余的全部组合。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int Max = 0x3f3f3f3f3f3f3f3f;
const int Min = -0x3f3f3f3f3f3f3f3f;
const int N = 2e5+5;
typedef pair<int, int>PII;
int T, n, m;

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> T;
    while(T --)
    {
        cin >> n >> m;
        int s = (m + 1) * (m + 2) / 2;
        int cnt[2] = {0};
        for(int i = 1; i <= n; i ++)
        {
            int x;
            cin >> x;
            s -= x / 2 + 1;
            s -= m - x + 1;
            cnt[x&1] ++;
            s += cnt[x&1];
        }
        cout << s << '\n';
    }
    return 0;
}

OJ

409组队赛1

K - 嘉然小姐的罗德岛干员生活

        题意很清楚,就是保证最短路径的情况下,使价值差最大。比赛的时候考虑到了三种情况

        在这种情况下舍弃哪一种情况都不可以,如果舍弃(7,10)那么后面如果有个价值 1,就会出错;如果舍弃(4,9)但是图就只是这样,也会出错;如果舍弃(3, 7),但是后面有个价值10,一样出错。所以在比赛的时候,我们存下了这三种情况,从头递推到尾。但是没想到真能 ac 了。

        代码写的非常复杂,由于边权是 1,一开始 bfs 找到从 1 到 n 的所有的最短路径并且标记,构建出一个新的图,由于最短路的性质,可以看作从 1 到 n 是一个有向图,然后利用拓扑排序递推维护这三种情况。

代码如下:

#include <bits/stdc++.h>
#define huoguo_fy ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
using namespace std;

#define endl '\n'
#define xx first
#define yy second
typedef long long LL;
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f;
const int N = 1e6 + 10;
int dis[N];
int n, m;
struct node
{
    int t1, z1, t2, z2, t3, z3;
} tr[N];

vector<PII> v[N], ve[N];
bool st[N], st1[N];
int ru[N];
queue<int> q;

void bfs(int u)
{
    q.push(u);
    st1[u] = true;
    while (q.size())
    {
        int tt = q.front();
        q.pop();
        for (int i = 0; i < v[tt].size(); i++)
        {
            int tv = v[tt][i].first, tv2 = v[tt][i].second;
            if (st1[tv])
            {
                if (dis[tv] == dis[tt] + 1)
                {
                    ve[tt].push_back({tv, tv2});
                }
                continue;
            }
            dis[tv] = dis[tt] + 1;
            // cout << tt << " " << tv << '\n';
            ru[tv]++;
            q.push(tv);
            st1[tv] = true;
            ve[tt].push_back({tv, tv2});
        }
    }
}
void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        tr[i] = {inf, -1, inf, -1, inf, -1};
    }
    for (int i = 1; i <= m; i++)
    {
        int x, y, z;
        cin >> x >> y >> z;
        v[x].push_back({y, z});
        v[y].push_back({x, z});
    }
    bfs(1);
    for (int i = 1; i <= n; i++)
    {
        if (ru[i] == 0)
            q.push(i);
    }
    while (q.size())
    {
        int t = q.front();
        q.pop();
        for (int i = 0; i < ve[t].size(); i++)
        {
            int tt = ve[t][i].first, tt1 = ve[t][i].second;
            if (tr[tt].t1 == -1 || tr[tt].z1 == -1)
            {
                tr[tt].t1 = min(tr[tt].t1, tt1);
                tr[tt].z1 = max(tr[tt].z1, tt1);
            }
            if (tr[tt].t2 == -1 || tr[tt].z2 == -1)
            {
                tr[tt].t2 = min(tr[tt].t2, tt1);
                tr[tt].z2 = max(tr[tt].z2, tt1);
            }
            if (tr[tt].t3 == -1 || tr[tt].z3 == -1)
            {
                tr[tt].t3 = min(tr[tt].t3, tt1);
                tr[tt].z3 = max(tr[tt].z3, tt1);
            }
            // if (t == 3 && tt == 4)
            // cout << min(tr[t].t1, tt1) << max(tr[t].z1, tt1);
            if (min(tt1, tr[t].t1) < tr[tt].t1 || (min(tt1, tr[t].t1) == tr[tt].t1 && max(tt1, tr[t].z1) > tr[tt].z1))
            {
                tr[tt].z1 = max(tr[t].z1, tt1);
                tr[tt].t1 = min(tr[t].t1, tt1);
            }
            if (max(tt1, tr[t].z2) > tr[tt].z2 || (max(tt1, tr[t].z2) == tr[tt].z2 && min(tt1, tr[t].t2) < tr[tt].t2))
            {
                tr[tt].z2 = max(tr[t].z2, tt1);
                tr[tt].t2 = min(tr[t].t2, tt1);
            }
            if (max(tt1, tr[t].z3) - min(tt1, tr[t].t3) > tr[tt].z3 - tr[tt].t3)
            {
                tr[tt].z3 = max(tr[t].z3, tt1);
                tr[tt].t3 = min(tr[t].t3, tt1);
            }
            ru[tt]--;
            if (ru[tt] == 0)
                q.push(tt);
        }
    }
    // for (int i = 1; i <= n; i ++)
    // {
    //     cout << tr[i].t1 << " " << tr[i].z1 << " " << tr[i].t2 << " " << tr[i].z2 << " " << tr[i].t3 << " " << tr[i].z3 << '\n';
    // }
    int ans = max(tr[n].z1 - tr[n].t1, max(tr[n].z2 - tr[n].t2, tr[n].z3 - tr[n].t3));
    cout << ans << '\n';
    // for (int i = 1; i <= n; i ++)
    // {
    //     for (int j = 0; j < ve[i].size(); j ++)
    //     {
    //         cout << ve[i][j].first << " ";
    //     }
    //     cout << '\n';
    // }
}
signed main()
{
    huoguo_fy;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

        题解思路倒是明白了,代码看的不是特别明白,所以自己思考了很久。思路就是正反两遍 dijkstra,维护出走到每个位置时的最短路径。然后遍历最短路径的两个直接相连的端点,得出答案。

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
typedef pair<int, int> PII;
int T, n, m;
vector<PII> v[N];
int dis[N][2], mi[N][2], mx[N][2];
bool st[N][2];
int ans = 0;

priority_queue<PII, vector<PII>, greater<PII>> q;
void dijkstra(int u, int op)
{
    dis[u][op] = 0;
    q.push({dis[u][op], u});
    while (q.size())
    {
        int t = q.top().second;
        q.pop();
        if (st[t][op])
            continue;
        st[t][op] = true;
        for (int i = 0; i < v[t].size(); i++)
        {
            int z1 = v[t][i].first, z2 = v[t][i].second;
            if (dis[z1][op] > dis[t][op] + 1)
            {
                dis[z1][op] = dis[t][op] + 1;
                mi[z1][op] = min({mi[t][op], mi[z1][op], z2});
                mx[z1][op] = max({mx[t][op], mx[z1][op], z2});
                q.push({dis[z1][op], z1});
            }
            else if (dis[z1][op] == dis[t][op] + 1)
            {
                mi[z1][op] = min({mi[t][op], mi[z1][op], z2});
                mx[z1][op] = max({mx[t][op], mx[z1][op], z2});
            }
        }
    }
}
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    while (m--)
    {
        int x, y, z;
        cin >> x >> y >> z;
        v[x].push_back({y, z});
        v[y].push_back({x, z});
    }
    memset(dis, 0x3f, sizeof dis);
    memset(mi, 0x3f, sizeof mi);
    dijkstra(1, 0);
    dijkstra(n, 1);
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j < v[i].size(); j++)
        {
            int t1 = i, t2 = v[i][j].first, z = v[i][j].second;
            if (dis[t1][0] + 1 + dis[t2][1] == dis[n][0])
            {
                int a1 = max(mx[t1][0], z) - min(mi[t2][1], z);
                int a2 = max(mx[t2][1], z) - min(mi[t1][0], z);
                ans = max({ans, a1, a2});
            }
        }
    } 
    cout << ans << '\n';
    return 0;
}

        但是通过遍历所有最短路的路径,来维护答案的这个过程非常难想到,也就是那两个 for 循环那里。正常来说,直接遍历最短路上的点来更新答案更容易想到。所以就要学会如何存点。对于一个最短路径,如果我们是存任意一条路径的话会比较容易,只需加上一句 path[v] = u。但是这个题明显要把所有最短路上的点都存下来,想到用 vector。

 相关链接:最短路的路径保存

代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
typedef pair<int, int> PII;
int T, n, m;
vector<PII> v[N];
int dis[N][2], mi[N][2], mx[N][2];
bool st[N][2];
vector<int>path[N];
int ans = 0;

priority_queue<PII, vector<PII>, greater<PII>> q;
void dijkstra(int u, int op)
{
    dis[u][op] = 0;
    q.push({dis[u][op], u});
    while (q.size())
    {
        int t = q.top().second;
        q.pop();
        if (st[t][op])
            continue;
        st[t][op] = true;
        for (int i = 0; i < v[t].size(); i++)
        {
            int z1 = v[t][i].first, z2 = v[t][i].second;
            if (dis[z1][op] > dis[t][op] + 1)
            {
                dis[z1][op] = dis[t][op] + 1;
                mi[z1][op] = min({mi[t][op], mi[z1][op], z2});
                mx[z1][op] = max({mx[t][op], mx[z1][op], z2});
                q.push({dis[z1][op], z1});
                if(op == 0)  //一定要注意 op == 0,因为会正反跑两边,不 if 就跑乱了
                {
                    path[z1].clear();
                    path[z1].push_back(t);
                }
            }
            else if (dis[z1][op] == dis[t][op] + 1)
            {
                mi[z1][op] = min({mi[t][op], mi[z1][op], z2});
                mx[z1][op] = max({mx[t][op], mx[z1][op], z2});
                if(op == 0) path[z1].push_back(t);
            }
        }
    }
}
void dfs(int u)
{
    int a1 = mx[u][0] - mi[u][1];
    int a2 = mx[u][1] - mi[u][0];
    ans = max({ans, a1, a2});
    for(int i = 0; i < path[u].size(); i ++)
    {
        dfs(path[u][i]);
    }
}
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    while (m--)
    {
        int x, y, z;
        cin >> x >> y >> z;
        v[x].push_back({y, z});
        v[y].push_back({x, z});
    }
    memset(dis, 0x3f, sizeof dis);
    memset(mi, 0x3f, sizeof mi);
    dijkstra(1, 0);
    dijkstra(n, 1);
    // 从 1 到 n 所有最短路径上的点
    dfs(n);
    cout << ans << '\n';
    return 0;
}

         最后再附上一份不断修改 bug 和思考过程的代码QAQ

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
typedef pair<int, int> PII;
int T, n, m;
vector<PII> v[N];
int dis[N][2], mi[N][2], mx[N][2];
bool st[N][2], sc[N];
vector<int>p[N];
int ans = 0;

priority_queue<PII, vector<PII>, greater<PII>> q;
void dijkstra(int u, int op)
{
    dis[u][op] = 0;
    q.push({dis[u][op], u});
    while (q.size())
    {
        int t = q.top().second;
        q.pop();
        if (st[t][op])
            continue;
        st[t][op] = true;
        for (int i = 0; i < v[t].size(); i++)
        {
            int z1 = v[t][i].first, z2 = v[t][i].second;
            if (dis[z1][op] > dis[t][op] + 1)
            {
                dis[z1][op] = dis[t][op] + 1;
                // mi[z1][op] = min({mi[t][op], z2});
                // mx[z1][op] = max({mx[t][op], z2});
                mi[z1][op] = min({mi[t][op], mi[z1][op], z2});
                mx[z1][op] = max({mx[t][op], mx[z1][op], z2});
                // cout << t << " " << z1 << " " << mi[z1][op] << " " << mx[z1][op] << '\n';
                q.push({dis[z1][op], z1});
                if(op == 0)
                {
                    p[z1].clear();
                    p[z1].push_back(t);
                }
            }
            else if (dis[z1][op] == dis[t][op] + 1)
            {
                // mi[z1][op] = min({mi[t][op], z2});
                // mx[z1][op] = max({mx[t][op], z2});
                mi[z1][op] = min({mi[t][op], mi[z1][op], z2});
                mx[z1][op] = max({mx[t][op], mx[z1][op], z2});
                if(op == 0) p[z1].push_back(t);
                // cout << t << " " << z1 << " " << mi[z1][op] << " " << mx[z1][op] << '\n';
            }
        }
    }
}
void dfs(int u)
{
    int a1 = mx[u][0] - mi[u][1];
    int a2 = mx[u][1] - mi[u][0];
    ans = max({ans, a1, a2});
    for(int i = 0; i < p[u].size(); i ++)
    {
        // if(sc[p[u][i]]) continue;
        // sc[p[u][i]] = true;
        dfs(p[u][i]);
    }
}
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    while (m--)
    {
        int x, y, z;
        cin >> x >> y >> z;
        v[x].push_back({y, z});
        v[y].push_back({x, z});
    }
    memset(dis, 0x3f, sizeof dis);
    memset(mi, 0x3f, sizeof mi);
    dijkstra(1, 0);
    // cout << '\n';
    dijkstra(n, 1);
    // cout << "\n";
    // 
    dfs(n);
    // cout << sc[1];
    // for(int i = 2; i <= n-1; i ++)
    // {
    //     int a1 = mx[i][0] - mi[i][1];
    //     int a2 = mx[i][1] - mi[i][0];
    //     ans = max({ans, a1, a2});
    // }
    // for (int i = 1; i <= n; i++)
    // {
    //     for (int j = 0; j < v[i].size(); j++)
    //     {
    //         int t1 = i, t2 = v[i][j].first, z = v[i][j].second;
    //         if (dis[t1][0] + 1 + dis[t2][1] == dis[n][0])
    //         {
    //             // int mxx = max({mx[t1][0], z, mx[t2][1]});
    //             // int mii = min({mi[t1][0], z, mi[t2][1]});
    //             int a1 = max(mx[t1][0], z) - min(mi[t2][1], z);
    //             int a2 = max(mx[t2][1], z) - min(mi[t1][0], z);
    //             // if(ans < mxx - mii)
    //             // {
    //             //     cout << t1 << " " << t2 << " " << mxx << " " << mii << '\n';
    //             //     cout << mx[t1][0] << " " << z << " " << mx[t2][1] << '\n';
    //             //     cout << mi[t1][0] << " " << z << " " << mi[t2][1] << "\n";
    //             // }
    //             ans = max({ans, a1, a2});
    //         }
    //     }
    // }
    cout << ans << '\n';
    return 0;
}
  • 27
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值