BZOJ 3173 [Tjoi2013] 最长上升子序列 解题报告

这个题感觉比较简单,但却比较容易想残。。

我不会用树状数组求这个原排列,于是我只好用线段树。。。毕竟 Gromah 果弱马。

我们可以直接依次求出原排列的元素,每次找到最小并且最靠右的那个元素,假设这是第 $i$ 次找的,那么这就是原排列的第 $i$ 项,然后我们就把这个元素删去(变成很大的数),再把这个数以左的数都加 1,进行下一轮。

然后就是裸的最长上升子序列啦~~~

时间复杂度 $O(n\log n)$,空间复杂度 $O(n)$。

  1 #include <cstdio>
  2 #include <algorithm>
  3 using namespace std;
  4 #define N 100000 + 5
  5 #define M 262144 + 5
  6 #define ls(x) x << 1
  7 #define rs(x) x << 1 | 1
  8
  9 int n, Pos[N], A[N], T[N], F[N];
 10
 11 struct Segment_Tree
 12 {
 13     int Min, delta;
 14 }h[M];
 15
 16 inline void Build(int x, int l, int r)
 17 {
 18     if (l == r)
 19     {
 20         h[x].Min = Pos[l];
 21         return ;
 22     }
 23     int mid = l + r >> 1;
 24     Build(ls(x), l, mid);
 25     Build(rs(x), mid + 1, r);
 26     h[x].Min = min(h[ls(x)].Min, h[rs(x)].Min);
 27 }
 28
 29 inline void apply(int x, int d)
 30 {
 31     h[x].Min += d, h[x].delta += d;
 32 }
 33
 34 inline void push(int x)
 35 {
 36     if (h[x].delta)
 37     {
 38         apply(ls(x), h[x].delta);
 39         apply(rs(x), h[x].delta);
 40         h[x].delta = 0;
 41     }
 42 }
 43
 44 inline void Modify(int x, int l, int r, int s, int t, int d)
 45 {
 46     if (l == s && r == t)
 47     {
 48         apply(x, d);
 49         return ;
 50     }
 51     push(x);
 52     int mid = l + r >> 1;
 53     if (t <= mid) Modify(ls(x), l, mid, s, t, d);
 54         else if (s > mid) Modify(rs(x), mid + 1, r, s, t, d);
 55         else Modify(ls(x), l, mid, s, mid, d), Modify(rs(x), mid + 1, r, mid + 1, t, d);
 56     h[x].Min = min(h[ls(x)].Min, h[rs(x)].Min);
 57 }
 58
 59 inline int Query(int x, int l, int r)
 60 {
 61     if (l == r) return l;
 62     int mid = l + r >> 1;
 63     if (h[rs(x)].Min <= h[ls(x)].Min)
 64         return Query(rs(x), mid + 1, r);
 65     else return Query(ls(x), l, mid);
 66 }
 67
 68 int main()
 69 {
 70     #ifndef ONLINE_JUDGE
 71         freopen("3173.in", "r", stdin);
 72         freopen("3173.out", "w", stdout);
 73     #endif
 74
 75     scanf("%d", &n);
 76     for (int i = 1; i <= n; i ++)
 77         scanf("%d", Pos + i);
 78     Build(1, 1, n);
 79     for (int i = 1; i <= n; i ++)
 80     {
 81         int t = Query(1, 1, n);
 82         Modify(1, 1, n, 1, t, 1);
 83         Modify(1, 1, n, t, t, n);
 84         if (!T[0] || T[T[0]] < t)
 85         {
 86             T[++ T[0]] = t;
 87             F[t] = T[0];
 88         }
 89         else
 90         {
 91             int x = lower_bound(T + 1, T + T[0] + 1, t) - T;
 92             T[x] = t;
 93             F[t] = x;
 94         }
 95     }
 96     for (int i = 1, Max = 0; i <= n; i ++)
 97     {
 98         Max = Max > F[i] ? Max : F[i];
 99         printf("%d\n", Max);
100     }
101
102     #ifndef ONLINE_JUDGE
103         fclose(stdin);
104         fclose(stdout);
105     #endif
106     return 0;
107 }

3173_Gromah

时间: 2024-11-05 04:47:23

BZOJ 3173 [Tjoi2013] 最长上升子序列 解题报告的相关文章

BZOJ 3173: [Tjoi2013]最长上升子序列 [splay DP]

3173: [Tjoi2013]最长上升子序列 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1613  Solved: 839[Submit][Status][Discuss] Description 给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.每插入一个数字,我们都想知道此时最长上升子序列长度是多少? Input 第一行一个整数N,表示我们要将1到N插入序列中,接下是N个数字,第k个数字Xk

BZOJ 3173: [Tjoi2013]最长上升子序列

3173: [Tjoi2013]最长上升子序列 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1524  Solved: 797[Submit][Status][Discuss] Description 给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.每插入一个数字,我们都想知道此时最长上升子序列长度是多少? Input 第一行一个整数N,表示我们要将1到N插入序列中,接下是N个数字,第k个数字Xk

BZOJ 3173: [Tjoi2013]最长上升子序列( BST + LIS )

因为是从1~n插入的, 慢插入的对之前的没有影响, 所以我们可以用平衡树维护, 弄出最后的序列然后跑LIS就OK了 O(nlogn) -------------------------------------------------------------------- #include<bits/stdc++.h> #define rep(i, n) for(int i = 0; i < n; ++i) #define clr(x, c) memset(x, c, sizeof(x))

bzoj 3173: [Tjoi2013]最长上升子序列(splay)

3173: [Tjoi2013]最长上升子序列 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 1315  Solved: 682 [Submit][Status][Discuss] Description 给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.每插入一个数字,我们都想知道此时最长上升子序列长度是多少? Input 第一行一个整数N,表示我们要将1到N插入序列中,接下是N个数字,第k个数字

BZOJ 3173 [Tjoi2013]最长上升子序列 Treap+LIS

题意:链接 方法: Treap+LIS 解析: 刚看到题还蒙了一会,无从下手啊- -! 后来寻思找找规律. 画画样例以及瞎编了两组数据之后发现点问题- 答案递增? 然后才反应过来- -,当我们从小到大插入的时候,插入后的情况是不影响插入前的. 所以这样的话我们可以把整个序列先弄出来? 之后求一个LIS,这时候对于每一个数来说,他都有个LIS值,当然这个可能不是他插入之后的最优解,因为之前的值可能比他插入之后还要大. 所以我们再需要扫一下数组,更新下答案即可. 至于第一部分找序列,用treap实现

BZOJ 3173 Tjoi2013 最长上升子序列 Treap+树状数组

题目大意:给定一个序列,依次将1~n插入,问每次插入之后序列的LIS长度是多少 由于是从小到大插入,因此插入一个数之后显然是不影响之前的答案的 因此我们不妨先用平衡树搞出插入之后的序列,再求一遍LIS即可 注意最后每个点还要对前面的取一下max 因为插入后LIS可能还是之前的序列 蒟蒻的我到底还是把平衡树写挂了... #include <cstdio> #include <cstring> #include <iostream> #include <algorit

最长递增子序列——解题报告

最长递增子序列--解题报告 题目描述:给定一个数组,长度为n,求出其中最长递增子序列. 分析:这题可以用动态规划求解,遍历i = 1 : n,当第i个数比前面j数大,而且前面的子序列长度加1之后,比现在的第i个子序列长的话,那么就变换.额,晦涩难懂额..看代码可以好一些. 代码如下: #include<iostream> using namespace std; // solution 1: dp int maxSubLength(int a[], int n) { int *LIS = ne

最长上升子序列解题报告

动规方程f[i]=f[j]+1(1<=j<i, a[j]<a[i]) 教训最长上升子序列有一个特点,就是答案最小都是1,所以可以给f[i]赋值为1,还有一种处理方式是并不赋值为1,但是在输出答案的时候加1就好了(其实就是偏移量).给每个f[i]加1其实就意味着每个数都可能是最长上升子序列的第一个元素. 然后我犯了一个错误,只给f[1]赋值为1,其实在算法上就意味着默认最长上升子序列是从第一个数开始,呃,错的.然后自己给数据就往往给的是从第一开始的答案,总是wrong.

最长公共子序列解题报告

if(x[i]==y[j]) f[i][j]=f[i-1][j-1]+1; else if(x[i]!=y[j]) f[i][j]=max(f[i-1][j], f[i][j-1]); 原决策 这一块其实动规方程是 f[i][j]=max(f[i-1][j-1]+(x[i]==y[j]), f[i][j-1], f[i-1][j]); 动规方程 可以联系到射箭一题 1 #include <stdio.h> 2 #define maxn 501 3 int x[maxn], y[maxn], f