跳转至

LIS 最长上升子序列

最长上升子序列

这是一个简单的动规板子题。

给出一个由 \(n(n\le 5000)\) 个不超过 \(10^6\) 的正整数组成的序列。请输出这个序列的**最长上升子序列**的长度。

最长上升子序列是指,从原序列中**按顺序**取出一些数字排在一起,这些数字是**逐渐增大**的。

int n,m,a[N],f[N];
int s[N],top;

signed main(){
    n=rd;
    f[0]=1;
    for(int i=1;i<=n;i++){
        a[i]=rd;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<i;j++){
            if(a[i]>a[j])f[i]=max(f[i],f[j]+1);
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++)ans=max(ans,f[i]);
    cout<<ans+1<<endl;
}

图上LIS | Pathwalks

给定 \(n\) 个点 \(m\) 条边的有向图,可能不连通,可能有重边,也可能会有自环。求最长的路径(可以经过重复节点),使得这条路径的编号和权值都**严格**单调递增,其中编号指输入的顺序。路径的长度是指经过边的数量。

输入格式

第一行两个整数 \(n,m\)

第二行到第 \(m+1\) 行,每行三个整数 \(a,b,k\),表示顶点 \(a\) 与顶点 \(b\) 有一条边相连,边权为 \(k\)

输出格式

一行一个整数,表示最长的路径的长度。

\(1\leq n,m\leq10^5\)\(0\le w_i\le10^5\)

retranslated by @皎月半洒花。


因为严格,所以每一条边至多被选中一次,因此不用考虑后效,可以dp

/*
CB Ntsc111
*/

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ull unsigned int
#define pii pair<int, int>
#define pf first
#define ps second

#define err cerr << "Error"
#define rd read()
// #define nl putc('\n')
#define ot write
#define nl putchar('\n')
int rd {
  int xx = 0, ff = 1;
  char ch = getchar();
  while (ch < '0' || ch > '9') {
    if (ch == '-')
      ff = -1;
    ch = getchar();
  }
  while (ch >= '0' && ch <= '9')
    xx = xx * 10 + (ch - '0'), ch = getchar();
  return xx * ff;
}
void write(int out) {
  if (out < 0)
    putchar('-'), out = -out;
  if (out > 9)
    write(out / 10);
  putchar(out % 10 + '0');
}

const int INF = 1e18;
const int N = 1e5 + 5;
const int M = 1e6 + 5;
const int maxlog = 10;

int ans;
int res;
int n, m;

map<int, int> mp[N];

int dfs(int x, int v) {
  int res = 0;
  auto p = mp[x].lower_bound(v);
  res = (p == mp[x].begin()) ? 0 : ((--p)->second);
  return res;
}

signed main() {
  n = rd, m = rd;
  for (int i = 1; i <= m; ++i) {
    int x = rd, y = rd, z = rd;
    res = dfs(x, z);
    ++res;
    if (res > dfs(y, z)) {
      auto p = mp[y].lower_bound(z);
      mp[y][z] = res;
      for (auto q = p; q != mp[y].end(); q = mp[y].erase(q)) {
        if ((q->second) > res)
          break;
      }
    }
    ans = std ::max(ans, res);
  }
  printf("%d\n", ans);
  return 0;
}

/*
4
()()
1 -1 5 11

4
()()
1 6 5 11
*/

不断插入的多次询问 | [TJOI2013] 最长上升子序列

给定一个序列,初始为空。现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置。每插入一个数字,我们都想知道此时最长上升子序列长度是多少?


我们发现,因为插入是从小到大插入的,所以我们先把最终的数组求出来,然后进行一次LIS。然后我们把以每一个位置i为结尾的LIS长度都求出来。

对于位置i的答案,设位置i的值为\(v_i\),我们只需要把所有j满足\(v_j≤v_i\)\(ans_j\)取最大值即可。因为i后面插入的其他数字对i及i前面插入的数字是没有影响的。

为了快速求出最后的序列,我们可以使用链表。当然,vector也是可以的,这里我们尝试使用vector来解决。


不要把线段树写错啊喂,最近又打反又漏pushdown的。

#include <bits/stdc++.h>
#include <queue>
#define rep(l, r, i) for (int i = l, END##i = r; i <= END##i; ++i)
#define per(r, l, i) for (int i = r, END##i = l; i >= END##i; --i)
using namespace std;
#define pb push_back
#define mp make_pair
#define int long long
#define pii pair<int, int>
#define ps second
#define pf first

#define X(j) i[j]
#define Y(j) (dp[j] + (i[j] + L) * (i[j] + L))

#define rd read()
int read() {
    int xx = 0, ff = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            ff = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
      xx = xx * 10 + (ch - '0'), ch = getchar();
    return xx * ff;
}
void write(int out) {
    if (out < 0)
        putchar('-'), out = -out;
    if (out > 9)
        write(out / 10);
    putchar(out % 10 + '0');
}

const int N = 3e5 + 5;
const int INF = 1e18;

int n;

int t[N<<2];
void pushup(int x,int l,int r){
    t[x]=max(t[x<<1],t[x<<1|1]);
    // cerr<<":pu "<<l<<' '<<r<<" v:"<<t[x]<<endl;
}

void change(int x,int l,int r,int p,int v){
    if(l==r){
        t[x]=v;
        return ;
    }
    int mid=l+r>>1;
    // cerr<<mid<<' '<<p<<endl;
    if(p<=mid)change(x<<1,l,mid,p,v);//NO mid<=p!!!
    else change(x<<1|1,mid+1,r,p,v);
    pushup(x,l,r);
}

int query(int x,int l,int r,int pl,int pr){
    if(pl>pr)return 0;
    if(l>=pl&&r<=pr){
        // cerr<<"return at:"<<l<<' '<<r<<"withv:"<<t[x]<<endl;
        return t[x];}
    int mid=l+r>>1;
    int res=0;
    if(pl<=mid)res=max(res,query(x<<1,l,mid,pl,pr));
    if(pr>mid)res=max(res,query(x<<1|1,mid+1,r,pl,pr));
    return res;
}
int g[N],f[N],ans[N];

void solve(){
    n=rd;
    vector<int> v;
    for(int i=1;i<=n;i++){
        int loc=rd;
        v.insert(v.begin()+loc,i);
    }
    // for(auto a:v)cerr<<a<<' ';

    for(int i=1;i<=n;i++){
        g[v[i-1]]=f[i]=query(1,1,n,1,v[i-1]-1)+1;
        // cerr<<"q:"<<query(1,1,n,1,v[i-1]-1)<<" at:"<<v[i-1]<<endl;
        // cerr<<"change at:"<<v[i-1]<<" v:"<<f[i]<<endl;
        change(1,1,n,v[i-1],f[i]);
    }
    // cerr<<endl;
    // for(int i=1;i<=n;i++)cerr<<f[i]<<endl;
    for(int i=1;i<=n;i++){
        ans[i]=max(ans[i-1],g[i]);
    }
    for(int i=1;i<=n;i++){
        cout<<ans[i]<<endl;
    }

}

signed main() {
    int T=1;
    while(T--){
        solve();
    }
    return 0;
}