ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2021-08-04

2021-08-04 21:31:21  阅读:144  来源: 互联网

标签:return 04 rs int 08 mid ls dp 2021


杭电2021中超联赛(4)补题报告(4)

1007
题源链接:杭电2021 题号1007
解题思路
定义 dp[i] 为 a[1…i] 中以 a[i] 为结尾的极长上升子序列个数。则 dp[i] 对 dp[j] 有贡献当且仅当i 到 j 之间没有 a[i] 到 a[j] 之间的元素。 dp[i] 初值为 1 当且仅当 a[i] 前面没有比 a[i] 小的。dp[i] 对答案有贡献当且仅当 a[i] 后面没有比 a[i] 大的。这样就有了一个朴素的O(n^2) 做法。
通过分治并对两侧分别维护单调栈,我们可以把时间复杂度优化到 O(nlog^2 n)。当处理区间 [l,r]时,令 m 为 [l,r] 的中点,对 a[l,r] 中所有元素按值依次从小到大处理。对 [l,m] 和 [m+1,r] 分别建单调栈。左边单调栈中的元素值从小到大,位置从大到小。右边的单调栈中元素值从小到大,位置从小到大处理左边的元素时直接往单调栈里丢,处理右边的元素 a[i] 时通过单调栈找到 [m+1,i-1] 中比他小的最靠右侧的元素 a[j] 。然后在左侧的单调栈中找到比 a[j] 大的第一个元素和比 a[i] 小的最后一个元素。
区间 [l,m] 对 dp[i] 的贡献就来自单调栈里这两个元素之间的元素的 dp 值之和。

以下给出AC代码:

#include <bits/stdc++.h>
using namespace std;

typedef pair<int, int> pii;

const int P = 998244353;
int add(int a, int b) { a += b; return a < P ? a : a - P; }
int sub(int a, int b) { a -= b; return a < 0 ? a + P : a; }

const int N = 100001;
int a[N], dp[N];

void solve(int l, int r) {
    if (l + 1 == r) return;
    int mid = (l + r) >> 1;
    solve(l, mid);
    vector<int> pos(r - l);
    iota(pos.begin(), pos.end(), l);
    sort(pos.begin(), pos.end(), [&](int i, int j) { return a[i] < a[j]; });
    vector<int> ls, rs;
    vector<int> sum(1, 0);
    for (int i : pos) {
        if (i < mid) {
            while (!ls.empty() && ls.back() < i)
                sum.pop_back(), ls.pop_back();
            ls.push_back(i);
            sum.push_back(add(sum.back(), dp[i]));            
        }
        else {
            while (!rs.empty() && rs.back() > i)
                rs.pop_back();
            if (ls.empty()) continue;
            int id1 = partition_point(ls.begin(), ls.end(), [&](int x) { return a[x] < a[i]; }) - ls.begin();
            int id2 = rs.empty() ? 0 : partition_point(ls.begin(), ls.end(), [&](int x) { return a[x] < a[rs.back()]; }) - ls.begin();
            dp[i] = add(dp[i], sub(sum[id1], sum[id2]));
            rs.push_back(i);
        }
    }
    solve(mid, r);
}

int main(void) {
    int T; scanf("%d", &T);
    while (T--) {
        int n; scanf("%d", &n);
        for (int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        for (int i = 1, v = INT_MAX; i <= n; ++i) {
            if (v > a[i])
                dp[i] = 1;
            else 
                dp[i] = 0;
            v = min(v, a[i]);
        }
        solve(1, n + 1);
        int ans = 0;
        for (int i = n, v = 0; i >= 1; --i) {
            if (v < a[i])
                ans = add(ans, dp[i]);
            v = max(v, a[i]);
        }
        printf("%d\n", ans);
    }
    return 0;
}

1008
题源链接:杭电2021 题号1008
解题思路:
考虑所有点的个数减去不能到达的点的个数,即为可以到达的点的个数。
根据题意,有地雷的地方是不可以到达的。由于僵尸只会向右和向下走,当某个点的左边和上方都不可达时,该点不可达,并会对自己右边的点和下方的点造成影响。由于空间很大但地雷数有限,可以从上往下逐行对每一行的地雷排序后进行处理。对每个地雷,找到从自己的右上角点(x-1,y+1)开始的从左往右的连续不可达区域的范围,那么 这行的这个范围也不可达。可以用线段树来实现区间查询和区间覆盖。每一行处理完后查询该行不可达的点数,累加后用总点数减即得到答案。

以下给出AC代码:

#include<bits/stdc++.h>
using namespace std;
#define ls (x<<1)
#define rs (x<<1|1)
const int N = 1e5 + 5;
const int inf = 0x3f3f3f3f;
vector<int>e[N];
int tr[2][N << 2], lz[2][N << 2];

void push_down(int f, int x, int l, int r, int mid) {
	if (lz[f][x] == -1)return;
	tr[f][ls] = lz[f][x] * (mid - l + 1);
	tr[f][rs] = lz[f][x] * (r - mid);
	lz[f][ls] = lz[f][rs] = lz[f][x];
	lz[f][x] = -1;
}
void update(int f,int x, int l, int r, int L, int R, int v) {
	if (L <= l && R >= r) {
		tr[f][x] = (r - l + 1) * v;
		lz[f][x] = v;
		return;
	}
	int mid = (l + r) >> 1;
	push_down(f, x, l, r, mid);
	if (R <= mid)update(f, ls, l, mid, L, R, v);
	else if (L > mid)update(f, rs, mid + 1, r, L, R, v);
	else {
		update(f, ls, l, mid, L, mid, v);
		update(f, rs, mid + 1, r, mid + 1, R, v);
	}
	tr[f][x] = tr[f][ls] + tr[f][rs];
}
int query(int f, int x, int l, int r, int L, int R) {
	if (!tr[f][x])return inf;
	if (l == r)return l;
	int mid = l + r >> 1;
	push_down(f, x, l, r, mid);
	if (L <= l && R >= r) {
		if (tr[f][ls] > 0) return query(f, ls, l, mid, l, mid);
		else return query(f, rs, mid + 1, r, mid + 1, r);
	}
	else {
		if (R <= mid)return query(f, ls, l, mid, L, R);
		else if (L > mid)return query(f, rs, mid + 1, r, L, R);
		else return min(query(f, ls, l, mid, L, mid), query(f, rs, mid + 1, r, mid + 1, R));
	}
}
int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		int n, m, k;
		scanf("%d %d %d", &n, &m, &k);
		for (int i = 1; i <= n; ++i)e[i].clear();
		for (int i = 1; i <= (m << 2); ++i) {
			tr[0][i] = tr[1][i] = 0;
			lz[0][i] = lz[1][i] = -1;
		}
		for (int i = 0; i < k; ++i) {
			int x, y;
			scanf("%d %d", &x, &y);
			e[x].push_back(y);
		}
		long long ans = 0;
		update(0, 1, 1, m, 1, 1, 1);
		for (int x = 1; x <= n; ++x) {
			int l = 0;
			sort(e[x].begin(), e[x].end());
			for (auto& y : e[x]) {
				if (y - 1 >= l + 1) {
					int pos = query((x & 1) ^ 1, 1, 1, m, l + 1, y - 1);
					if (pos != inf)update(x & 1, 1, 1, m, pos, y - 1, 1);
				}
				l = y;
			}
			if (l + 1 <= m) {
				int pos = query((x & 1) ^ 1, 1, 1, m, l + 1, m);
				if (pos != inf)update(x & 1, 1, 1, m, pos, m, 1);
			}
			ans += tr[x & 1][1];
			update((x & 1) ^ 1, 1, 1, m, 1, m, 0);
		}
		printf("%lld\n", ans);
	}
	return 0;
}

标签:return,04,rs,int,08,mid,ls,dp,2021
来源: https://blog.csdn.net/m0_51308094/article/details/119392879

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有