杂题随笔 10.31 两道LIS相关的题
https://www.luogu.com.cn/problem/AT_abc354_f
题意:给定一个序列a,求出所有的i使得任意一个a的最长子序列包含i。
解法:我们先求这个序列的LIS的长度maxx,然后再去正着求一遍最长上升子序列和反着求一遍最长下降子序列即可,如果拼起来等于maxx那么说明i这个点是满足要求的点。
注意细节的处理,反着求最长下降子序列相当于把原序列取反后reverse一下然后再求一遍LIS。
vector<int> get(const std::vector<int> &a) {
const int n = a.size();
std::vector<int> f(n);
std::vector<int> g;
for (int i = 0; i < n; i++) {
auto it = std::lower_bound(g.begin(), g.end(), a[i]);
f[i] = it - g.begin();
if (it == g.end()) {
g.push_back(a[i]);
} else {
*it = a[i];
}
}
return f;
}
void solve()
{
int n;
cin>>n;
vector<int> a(n);
for(int i=0;i<n;i++)
{
cin>>a[i];
}
auto f = get(a);
reverse(all(a));
for(auto &x:a)
{
x*=-1;
}
auto g = get(a);
reverse(all(g));
for(int i=0;i<n;i++)
{
f[i]+=g[i];
}
int maxx = *max_element(all(f));
vector<int> ans;
for(int i=0;i<n;i++)
{
if(f[i]==maxx)
{
ans.push_back(i+1);
}
}
cout<<(int)ans.size()<<"\n";
for(auto x:ans)
{
cout<<x<<" ";
}
cout<<'\n';
}
https://www.luogu.com.cn/problem/AT_arc149_b
题意:给定两个序列a,b,选一个下标i,swap(a[i],a[i+1]) swap(b[i],b[i+1]),可以操作任意次,问LIS(a)+LIS(b)的值最大是多少。
解法:如果我们位于i,我们考虑三种情况:
1.换了之后LIS(a)+1,LIS(b)-1,那么这种操作对答案的贡献肯定没有影响,可以换
2.换了之后LIS(a)+1,LIS(b)+1 贡献为正,可以换
3.两者都是-1 ,贡献为负,不换。
考虑到交换操作是两序列同步进行的,那么若a序列以最好的可能交换,是不会减少两序列总体对答案的贡献的。因为两个序列都是排列,将b映射到a上,再将a从小到大排序即可。
int get(auto a)
{
vector<int> f;
for(auto x:a)
{
auto it = lower_bound(all(f),x);
if(it==f.end())
{
f.push_back(x);
}
else
{
*it = x;
}
}
return f.size();
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
vector<int> a(n),b(n);
for(int i=0;i<n;i++)
{
cin>>a[i];
}
for(int i=0;i<n;i++)
{
cin>>b[a[i]-1];
}
int len = get(b);
cout<<len+n<<'\n';
}
注意从下标为0输入,输入b[a[i]-1].