HDU - 5324 Boring Class

cdq分治+树状数组

很久没写cdq分治了,大概有两年了?

求答案最小字典序有个小trick,倒着求一个f[i]表示以i开始最长的序列长度,最后输出的时候从前往后扫f,第一个等于答案且满足前面已经选定的点带来的限制的点就可以添加到答案序列里来。

cdq分治求解中先处理后半部分,再计算后半部分的贡献,再计算前半部分。注意,计算贡献需要按某一维排序,然后类似归并地处理,用bit来取max,但递归前半部分时需要恢复序列按id的增序。

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

const int MAXN = 5e4 + 100;

struct Point {
    int x, y;
    int id;
} p[MAXN];
int f[MAXN];

bool cmp(const Point&a, const Point&b) {
    return a.id < b.id;
}
bool cmp2(const Point&a, const Point&b) {
    return a.x < b.x || (a.x == b.x && (a.y > b.y || (a.y == b.y && a.id < b.id)));
}

int b[MAXN * 2];
void upd(int p, int v) {
    //b[p] = v;
    //return ;
    for (; p < MAXN * 2; b[p] = max(b[p], v), p += p&-p);
}
void clr(int p) {
    //b[p] = 0;
    //return ;
    for (; p < MAXN * 2; b[p] = 0, p += p&-p);
}
int qry(int p) {
    int r = 0;
    //for (int i = 1; i <= p; ++i)
    //    r = max(r, b[i]);
    //return r;
    for (; 0 < p; r = max(r, b[p]), p -= p&-p);
    return r;
}

void cdq(int l, int r) {
    if (l == r) return ;
    int m = (l + r) >> 1;
    cdq(m + 1, r);
    sort(p + m + 1, p + r + 1, cmp2);
    sort(p + l, p + m + 1, cmp2);
    for (int i = m, j = r; l <= i; --i) {
        while (m < j && p[i].x <= p[j].x) upd(p[j].y, f[p[j].id]), --j;
        f[p[i].id] = max(f[p[i].id], 1 + qry(p[i].y));
    }
    for (int j = r; m < j; --j)
        clr(p[j].y);
    sort(p + l, p + m + 1, cmp);
    cdq(l, m);
}

int main() {
#ifdef lol
    freopen("code.in", "r", stdin);
    freopen("code.out", "w", stdout);
#endif

    int n;
    while (scanf("%d", &n) != EOF) {
        int c = 0, *t = new int[n * 2];
        for (int i = 1; i <= n; ++i)
            scanf("%d", &p[i].y), t[c++] = p[i].y;
        for (int i = 1; i <= n; ++i)
            scanf("%d", &p[i].x), t[c++] = p[i].x;
        sort(t, t + c);
        for (int i = 1; i <= n; ++i) {
            p[i].x = lower_bound(t, t + c, p[i].x) - t + 1;
            p[i].y = lower_bound(t, t + c, p[i].y) - t + 1;
            p[i].id = i;
            f[i] = 1;
        }
        delete t;
        cdq(1, n);
        sort(p + 1, p + n + 1, cmp);
        int ans = 0;
        for (int i = 1; i <= n; ++i) {
            ans = max(ans, f[i]);
        }
        printf("%d\n", ans);
        for (int i = 1, j = 0; i <= n; ++i)
            if (f[i] == ans && (j == 0 || (p[j].x <= p[i].x && p[i].y <= p[j].y))) {
                printf("%d%c", i, " \n"[(--ans) == 0]);
                j = i;
            }
    }
    return 0;
}
时间: 2024-10-09 17:32:32

HDU - 5324 Boring Class的相关文章

HDU 5324 Boring Class【cdq分治】

这就是一个三维排序的问题,一维递减,两维递增,这样的问题用裸的CDQ分治恰好能够解决. 如同HDU 4742(三维排序,一个三维都是递增的) 由于最小字典序比較麻烦,所以要从后面往前面做分治.每一个点的dp值表示以这个点为起点.最长能够延伸多长. 本来是想依照Li排序,可是这样做在cdq的时候实在是太难以处理了.所以就依照idx排序.也就是不须要排序. 然后依照Ri排序,对于左边,保证右边的每一个R值大于左边的值.并用树状数组维护Li(由于Li须要递减.所以树状数组恰好能够维护右边的Li小于左边

HDU 5008 Boring String Problem(西安网络赛B题)

HDU 5008 Boring String Problem 题目链接 思路:构造后缀数组,利用height的数组能预处理出每个字典序开始的前缀和有多少个(其实就是为了去除重复串),然后每次二分查找相应位置,然后在往前往后找一下sa[i]最小的 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int

HDU - 4961 Boring Sum

Problem Description Number theory is interesting, while this problem is boring. Here is the problem. Given an integer sequence a1, a2, -, an, let S(i) = {j|1<=j<i, and aj is a multiple of ai}. If S(i) is not empty, let f(i) be the maximum integer in

hdu 4961 Boring Sum(高效)

题目链接:hdu 4961 Boring Sum 题目大意:给定ai数组; 构造bi, k=max(j|0<j<i,aj%ai=0), bi=ak; 构造ci, k=min(j|i<j≤n,aj%ai=0), ci=ak; 求∑i=1nbi?ci 解题思路:因为ai≤105,所以预先处理好每个数的因子,然后在处理bi,ci数组的时候,每次遍历一个数,就将其所有的因子更新,对于bi维护最大值,对于ci维护最小值. #include <cstdio> #include <c

HDU 4961 Boring Sum 暴力

题意:对于所有的A[I],同时找到左边和右边离它最近且是它的倍数的数相乘最后加起来求和. 解题思路:n*sqrt(n)的算法,开始以为过不了,wa了两发因为lld I64d对拍一个小时发现一个小时前交的代码没错只是没变I64d,..具体思路是枚举每个a[i]的因子,找离它最近的那个更新,如果已经没更新就不用更新了.用两个辅助数组记录最近的就行. 解题代码: 1 // File Name: 1002.cpp 2 // Author: darkdream 3 // Created Time: 201

HDU 4961 Boring Sum 构造题

用一个数组c, c[i]表示i这个数出现的最近数字是几. 那么当加入一个6,则 c[1] = c[2] = c[3] = c[6] = 6; ==最近怎么都要开挂啊.. #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int N = 100005; inl

hdu 5008 Boring String Problem(后缀数组)

题目链接:hdu 5008 Boring String Problem 题目大意:给定一个字符串,初始状态l,r为0,每次询问子串中字典序第l^r^v+1的子串区间,对于重复的输出下标小的. 解题思路:后缀数组,对给定字符串做后缀数组,然后根据height数组确定每个位置做为起点的子串有多少,然后二分查找确定起点位置,但是因为子串的重复的要输出下表小的,所以确定起点后还要确定字典序最小的下标. #include <cstdio> #include <cstring> #includ

hdu 3518 Boring counting(后缀数组)

Boring counting                                                                       Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem Description 035 now faced a tough problem,his english teacher gives him

hdu 5008 Boring String Problem(后缀自动机构造后缀树)

hdu 5008 Boring String Problem(后缀自动机构造后缀树) 题意:给出一个字符串s,然后每次询问一个k,求s的所有子串中,字典序第k小的是谁?多个解,则输出最左边的那个 解题思路:这道题应该是为后缀树量身定制的吧.只要构造出了后缀树,然后按字典序遍历就可以得出每个节点包含的子串的字典序的范围了,而且必然是个连续的区间范围.但是我不会后缀树啊..比赛的时候突然想到,后缀自动机是可以构造后缀树的,虽然以前没写过,但还是硬着头皮上吧,居然还真的让我给撸出来了.我的做法是这样的