23.7.31 牛客暑期多校5部分题解

E - Red and Blue and Green

题目大意

构造一个长度为 n n n 的序列,满足 m m m 个条件,每个条件包含三个数 l , r , w l,\space r,\space w l, r, w,表示区间左端点,区间右端点,这个区间的逆序对数的奇偶性,保证两个区间包含或者不相交

解题思路

因为区间两两非包含即相交,可以搞出来一棵树的样子,那么遍历区间直接变成遍历树

对于每个遍历的区间,在下方所有区间都满足的情况下,下一级区间奇偶性异或和如果相等则什么操作都不用

如果不相等,那么可以尝试是否能将其分为两个互不相干的区间

可以的话只要将去和把前者的区间的最大值和后者区间的最小值交换即可

上述操作可以将区间的逆序对数增加 1 1 1(因为两者之间的数都比两者小),使奇偶性改变

code

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1009;
struct lol {int l, r, w;} h[N];
struct dot {int x, y;} e[N];
bool cmp(lol a, lol b) {return a.l < b.l || (a.l == b.l && a.r > b.r);}
int n, m, a[N], top[N], ans, v[N];
stack <int> st;
void ein(int x, int y)
{
    e[++ ans].x = top[x];
    e[ans].y = y;
    top[x] = ans;
}
void dfs(int x)
{
    v[x] = 1;
    int op = 0, fl = 0, nl = h[x].l, nr = h[x].r;
    for (int i = top[x]; i; i = e[i].x)
    {
        int y = e[i].y;
        nl = max(nl, h[y].l);
        nr = min(nr, h[y].r);
        op ^= h[y].w;
        fl = 1;
    }
    if (op != h[x].w)
    {
        if (fl == 0 && nl != nr) {swap(a[nl], a[nl + 1]);}
        else if (nl > h[x].l) {swap(a[nl], a[nl - 1]);}
        else if (nr < h[x].r) {swap(a[nr], a[nr + 1]);}
        else {printf("-1"); exit(0);}
    }
    for (int i = top[x]; i; i = e[i].x)
    {
        int y = e[i].y;
        dfs(y);
    }
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; ++ i)
        scanf("%d%d%d", &h[i].l, &h[i].r, &h[i].w);
    sort(h + 1, h + m + 1, cmp);
    for (int i = 1; i <= m; ++ i)
    {
        while (!st.empty() && h[st.top()].r < h[i].r) st.pop();
        if (!st.empty()) ein(st.top(), i);
        st.push(i);
    }
    for (int i = 1; i <= n; ++ i) a[i] = i;
    for (int i = 1; i <= m; ++ i)
        if (!v[i]) dfs(i);
    for (int i = 1; i <= n; ++ i) printf("%d ", a[i]);
    return 0;
}

B - Circle of Mistery

题目大意

有一个长度为 n n n 的数列 { a i } \{a_i\} {ai},可以进行连边操作,使第 i i i 个数指向 p i p_i pi,这样操作会形成若干个环,要使存在一个环的 a i a_i ai 之和大于等于 k k k,问如何设计 p i p_i pi 才能使其逆序对数最少,输出最少的逆序对数或不可行

解题思路

当 k ≤ 0 k\le0 k≤0 时,只要找有没有比它大的数即可

当 k > 0 k>0 k>0 时,先进行规律寻找

发现你要求出来的最优解一定是找一个区间形成一个环并且选择去掉其中一些负数

逆序对数是区间长度减 1 1 1 再加上去掉的数的个数

假设先把左端点确定去枚举右端点,并用优先队列存其中的负数方便处理

如果维护的区间和大于等于 k k k,可以从优加入去掉的负数(绝对值小的优先)

如果维护的区间和小于 k k k,可以将其中的负数从优去掉(绝对值大的优先)

如果负数加多了就回退一个,如果和不够就继续枚举下一位

在上述过程中维护顺带维护答案并在合法情况下求最小值即可

code

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 9;
const int INF = 1e9;
int n, k, ans, a[N];
priority_queue <int> q, q1;
//q大根堆取负以维护小根堆,记录在答案中的负数,q1维护剔除的负数
int main() {
    scanf("%d%d", &n, &k); ans = INF;
    for (int i = 1; i <= n; ++ i)
    	scanf("%d", &a[i]);
    for (int i = 1; i <= n; ++ i) {
    	int cnt = 0, tmp = -1, num = 0;
    	while (!q.empty()) q.pop();
    	while (!q1.empty()) q1.pop();
        for (int j = i; j <= n; ++ j) {
        	++ tmp; ++ cnt; num += a[j];//tmp为维护的答案,cnt为在此答案用的数的个数
        	if (a[j] < 0) q.push(-a[j]);
        	while (!q1.empty() && num + q1.top() >= k)//两个顺序不能反,这样才能保证维护合法方案
        		q.push(-q1.top()), num += q1.top(), q1.pop(), -- tmp, ++ cnt;
        	while (!q.empty() && num < k)
        		q1.push(-q.top()), num += q.top(), q.pop(), ++ tmp, -- cnt;
        	if (num >= k && cnt) ans = min(ans, tmp);
        	//要确定当前区间和大于等于k并且有数在答案中,将k<=0的情况一同处理
        }
    }
    printf("%d", ans == INF ? -1 : ans);
    return 0;
}
相关推荐
哪 吒3 天前
华为OD机试 - 打印机队列 - 优先队列(Java 2024 E卷 200分)
java·开发语言·华为od·优先队列
图王大胜5 天前
模型 定位地图
人工智能·思维·模型·定位·商业·交叉学科
硕风和炜12 天前
【LeetCode:264. 丑数 II + 小根堆】
java·数学·算法·leetcode·优先队列·最小堆
Tisfy17 天前
LeetCode 0910.最小差值 II:贪心(排序)-小数大数分界线枚举(思考过程详解)
算法·leetcode·题解·贪心·枚举·思维·排序
文火冰糖的硅基工坊1 个月前
[产品管理-41]:什么是变量、属性、特征、特性?他们的相同点、不同点?
产品·创业·思维
文火冰糖的硅基工坊1 个月前
[产品管理-39]:什么是指标、规格、标准?什么是产品指标?什么是产品的设计指标? 什么是产品的技术指标?
产品经理·产品·思维·流程·战略
文火冰糖的硅基工坊1 个月前
[产品管理-33]:实验室技术与商业化产品的距离,实验室技术在商业化过程中要越过多少道“坎”?
产品·管理·创业·思维·战略
Tisfy1 个月前
LeetCode 2207.字符串中最多数目的子序列:计数
算法·leetcode·字符串·题解·思维·计数
文火冰糖的硅基工坊2 个月前
[创业之路-146] :如何理解:复杂的事情简单化,简单的事情标准化,标准的事情流程化,流程的事情数字化,数字化的事情自动化,自动化的事情智能化
自动化·管理·创业·思维
Tisfy3 个月前
LeetCode 2844.生成特殊数字的最少操作:模拟(分析)
linux·算法·leetcode·题解·模拟·思维