CSP-S 2021解题分析

        提高组初赛还是蛮难的,我压着56分的线过来的

1.廊桥分配

        今年T1也是特别的水,导致后三题比较难

        首先有一个关键结论:如果给廊桥编上号,每当一架飞机到达后,如果强制让它进入编号最小的廊桥,这样不会影响进入廊桥飞机的集合,且当廊桥总数增加后,原本在哪个廊桥的飞机,仍旧会进入原来的廊桥。

这个性质也很好理解:新加入廊桥可以视为原先的远机位的一部分,进入新加的这个廊桥的飞机可以视为在原来该进入远机位的飞机中贪心选择一轮。

至此本题已经有了优秀的解决方案:处理出每架飞机在廊桥充足的时候会进入哪个廊桥,然后在差分数组上统计一下即可。

考虑如何实现这个预处理过程:

考虑新建一个时间堆和空闲的编号堆,把所有的飞机按照到达时间排序,如果编号堆中有剩余就取出最小的编号,绑上这家飞机的离开时间扔进时间堆中,每当一架飞机进入之前,先把已经超过离开时间的飞机弹出,释放空闲的廊桥即可。

时间复杂度 O(n\log n)

#include <bits/stdc++.h>

using namespace std;

const int Maxn = 1e5 + 5;

struct e {
	int st, ed;
} a[Maxn];
bool cmp(e x, e y) {
	return x.st < y.st;
}

std::priority_queue<int> hp_id;
std::priority_queue<std::pair<int, int> > hp_ed; 

int n, m1, m2, ca[Maxn], cb[Maxn];

int main() {
	scanf("%d%d%d", &n, &m1, &m2);
	for(int i = 1; i <= m1; ++i) scanf("%d%d", &a[i].st, &a[i].ed);
	std::sort(a + 1, a + m1 + 1, cmp);
	for(int i = 1; i <= n; ++i) hp_id.push(-i);
	for(int i = 1; i <= m1; ++i) {
		if(!hp_ed.empty()) {
            while(-hp_ed.top().first < a[i].st) {
                hp_id.push(-hp_ed.top().second), hp_ed.pop();
                if(hp_ed.empty()) break;
            }
        }
        if(!hp_id.empty()) {
			int x = -hp_id.top(); hp_id.pop();
			ca[x]++; hp_ed.push(std::make_pair(-a[i].ed, x));
		}
	}
    for(int i = 1; i <= n; ++i) ca[i] += ca[i - 1];
	while(!hp_ed.empty()) hp_ed.pop(); while(!hp_id.empty()) hp_id.pop();
	for(int i = 1; i <= m2; ++i) scanf("%d%d", &a[i].st, &a[i].ed);
	std::sort(a + 1, a + m2 + 1, cmp);
	for(int i = 1; i <= n; ++i) hp_id.push(-i); 
	for(int i = 1; i <= m2; ++i) {
		if(!hp_ed.empty()) {
            while(-hp_ed.top().first < a[i].st) {
                hp_id.push(-hp_ed.top().second), hp_ed.pop();
                if(hp_ed.empty()) break;
            }
        }
		if(!hp_id.empty()) {
			int x = -hp_id.top(); hp_id.pop();
			cb[x]++; hp_ed.push(std::make_pair(-a[i].ed, x));
		}
	}
	for(int i = 1; i <= n; ++i) cb[i] += cb[i - 1]; 
	
	int mx_ans = 0;
	for(int i = 0; i <= n; ++i) mx_ans = std::max(mx_ans, ca[i] + cb[n - i]);
	printf("%d", mx_ans);
	
	return 0;
}

2.括号序列    

        首先肯定是区间dp,令 dp_{i,j}表示从位置i到位置j一共的合法序列总情况数量。

但是不同的形态可能会有不同的转移,如:(S)这种只能从S转移过来等等。所以只开两维的dp状态必然是不够的。

直接将方法吧。将两位的dp扩充为三维,第三位表示不同的形态种类,dp状态就变成了 dp_{i,j,[0,5]}。没种状态表示:

  • dp_{i,j,0}​: 形态如***...*的括号序列(即全部是*)。

  • dp_{i,j,1}​: 形态如(...)的括号序列(即左右直接被括号包裹且最左边括号与最右边的括号相互匹配)。

  • dp_{i,j,2}​: 形态如(...)**(...)***的括号序列(即左边以括号序列开头,右边以*结尾)。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值