zoukankan      html  css  js  c++  java
  • luoguP4093、BZOJ4553[HEOI2016&TJOI2016]序列

    题目链接

    洛谷

    BZOJ

    建议交BZOJ,洛谷数据比较弱

    解析

    一看到这种长得跟最长上升子序列很像的东西就想到dp

    不难写出dp方程:

    [dp[i] = max_{j < i, val[j] le min[i],max[j] le val[i]} {dp[j] + 1} ]

    其中(max[i]),(min[i]),(val[i])分别表示序列第(i)位的最大取值、最小取值和初始值

    然后观察限制条件,发现是个三位偏序问题,就可以套CDQ了。。。。

    代码(含注释)

    PS.算是第一道自己想出来的CDQ吧,我好菜啊QAQ

    再PS.CDQ都不会写了,最开始树状数组清零直接memset,结果T飞QAQ

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define MAXN 100005
    
    typedef long long LL;
    struct Node {
    	int val, mx, mn, id;
    	bool operator <(const Node &t) const { return mn < t.mn; }
    } a[MAXN], b[MAXN];
    int dp[MAXN], tree[MAXN];
    int N, M, ans;
    
    void solve(int, int);
    void update(int, int);
    int query(int);
    int main() {
    	std::ios::sync_with_stdio(false);
    	std::cin >> N >> M;
    	for (int i = 1; i <= N; ++i) {
    		std::cin >> a[i].val;
    		a[i].mx = a[i].mn = a[i].val;
    		a[i].id = i;
    	}
    	while (M--) {
    		int x, y;
    		std::cin >> x >> y;
    		a[x].mx = std::max(a[x].mx, y);
    		a[x].mn = std::min(a[x].mn, y);
    	}
    	//保证分治的时候右半区间mn有序 
    	std::sort(a + 1, a + 1 + N);
    	solve(1, N);
    	for (int i = 1; i <= N; ++i)
    		ans = std::max(ans, dp[i]);
    	std::cout << ans << std::endl;
    	return 0;
    }
    void solve(int l, int r) {
    	if (l == r) {
    		dp[a[l].id] = std::max(dp[a[l].id], 1);
    		return;
    	}
    	int mid = (l + r) >> 1, p1, p2;
    	
    	//保证左半区间位置在右半区间之前,对应条件一 
    	p1 = l, p2 = mid + 1;
    	for (int i = l; i <= r; ++i)
    		if (a[i].id <= mid) b[p1++] = a[i];
    		else b[p2++] = a[i];
    	for (int i = l; i <= r; ++i)
    		a[i] = b[i];
    	
    	//先算出左半区间的dp值,并按val排序 
    	solve(l, mid);
    	
    	//更新右半区间的dp值 
    	p1 = l, p2 = mid + 1;
    	while (p2 <= r) {
    		while (p1 <= mid && a[p1].val <= a[p2].mn)//保证条件二成立 
    			update(a[p1].mx, dp[a[p1].id]), ++p1;
    		dp[a[p2].id] = std::max(dp[a[p2].id], query(a[p2].val) + 1);//这一行和上面一行保证条件三成立 
    		++p2;
    	}
    	for (int i = l; i <= mid; ++i)
    		update(a[i].mx, 0);//还原树状数组,直接memset会T飞。。。 
    	
    	//继续计算右半区间的dp值,并按val排序 
    	solve(mid + 1, r);
    	
    	//将[l,r]按val排序,过程类似归并排序 
    	p1 = l, p2 = mid + 1;
    	for (int i = l; i <= r; ++i)
    		if (p1 > mid) b[i] = a[p2++];
    		else if (p2 > r) b[i] = a[p1++];
    		else b[i] = (a[p1].val < a[p2].val ? a[p1++] : a[p2++]);
    	for (int i = l; i <= r; ++i)
    		a[i] = b[i];
    }
    inline void update(int pos, int v) {
    	for (int i = pos; i < MAXN; i += (i & -i))
    		if (v) tree[i] = std::max(tree[i], v);
    		else tree[i] = 0;
    }
    inline int query(int pos) {
    	int res = 0;
    	for (int i = pos; i; i -= (i & -i))
    		res = std::max(tree[i], res);
    	return res;
    }
    
  • 相关阅读:
    煲鸡汤流程
    面向对象
    程序员英语学习思维导图
    百度通配符学习
    面向对象
    IO学习
    理解java的三大特性之继承
    重载(overload)、覆盖(override)、隐藏(hide)的区别
    2018年值得关注的10大JavaScript动画库
    小知识点总结
  • 原文地址:https://www.cnblogs.com/Rhein-E/p/10419435.html
Copyright © 2011-2022 走看看