题意:给n个数字表示一个长度为n的数组a,再给出一个长度为n的数组k,k[i]
表示数组a的a[k[i]]
在第i时刻后可用。输出n个数,表示第i个时刻的最长上升子序列的长度。a数组和k数组都是1~n的随机排列,n<5e4
题解:复杂度分析题。首先需要知道一个随机排列的期望LIS长度是 的(为什么是?出题人说是就是了 参考 )那么暴力地从后往前做这件事:如果碰到一个元素不可用而这个元素在这个时刻已经不可用了,那么就重新跑一遍LIS,这件事的概率 , 从而总复杂度
顺便还要记录一下每个元素的前一个元素是什么,从而得到整个LIS有哪些元素
#include <algorithm>
#include <iostream>
#include <unordered_set>
#include <vector>
using namespace std;
using pii = pair<int, int>;
const int maxn = 5e4 + 10;
int arr[maxn];
int k[maxn];
int x[maxn];
unsigned long ans[maxn];
int vis[maxn];
int n;
unordered_set<int> lis(int clk)
{
vector<int> dp;
dp.push_back(0);
for (int i = 1; i <= n; i++)
{
if (k[i] > clk)
continue;
auto it = lower_bound(dp.begin(), dp.end(), arr[i]);
if (it == dp.end())
{
vis[arr[i]] = dp.back();
dp.push_back(arr[i]);
}
else
{
*it = arr[i];
it--;
vis[arr[i]] = *it;
}
}
unordered_set<int> res;
for (int i = dp.back(); i; i = vis[i])
{
res.insert(i);
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
int _;
cin >> _;
while (_--)
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> arr[i];
}
for (int i = 1; i <= n; i++)
{
cin >> x[i];
k[x[i]] = i;
}
auto res = lis(n);
ans[n] = res.size();
for (int i = n; i > 1; i--)
{
int val = arr[x[i]];
if (res.count(val))
{
res = lis(i - 1);
}
ans[i - 1] = res.size();
}
for (int i = 1; i <= n; i++)
{
cout << ans[i] << " \n"[i == n];
}
}
}