P5156 [USACO18DEC]Sort It Out 题解&&LIS 统计 学习笔记

LIS数量统计

设f[i]代表以第i个数字为开头的LIS长度,g[i]代表方案数
转移时从后往前 f[i]=f[i+1···n]中的最大值 g[i]为最大长度的方案数的和
用树状数组维护即可,若需要维护以第i个数字为结尾的最长的LIS长度,正的做即可
(代码和下面题解和在一起了,感觉还算简单)

P5156 [USACO18DEC]Sort It Out 题解

题目中的排序方式类似于快速排序,不会改变其他数的相对位置
类似于之前模拟赛中的卡片重新插入排序类似的是,求出其最长子序列的长度,然后由n减去它即是集合大小
但本题还需要我们求出字典序第K小的集合,即求字典序第K大的LIS,但这样会给下面的操作带来麻烦(不转也可以,但我看不懂不转的代码)
将a(i)=x转化为b(x)=i 即由下标映射权值转化为权值映射下标,则字典序第K大转换为下标字典序第K大方便接下来的操作(保证vector内有序(虽然听说不转换vector内依旧有序但我不会证明))
然后我们用vector(或链表)来维护长度为 k 的LIS的开头有哪些,然后从长度k到1进行枚举
若以当前枚举到的数字为开头的LIS方案数比k小则这个数字不能选(因为选了之后就算是最大的字典序也无法达到k)若比k大则往下选。
注意:当前枚举到的数不能比之前枚举到的数还要小,需要特判
这题由于权值和下标老是转来转去,故将详细的解释放在代码的注释里(其实是我自己理解能力低下)

注意:代码中的下标和权值为了方便理解,标的是依据b的下标和权值。实际上b的下标是a的权值,b的权值是a的下标

#include<bits/stdc++.h>
using namespace std;
const long long inf=1e18;
long long read()
{
    long long tot=0,fs=1;
    char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if(ch=='-') fs=-1,ch=getchar();
    while(ch>='0'&&ch<='9') tot=tot*10+ch-'0',ch=getchar();
    return tot*fs;     
}
long long n,m;
long long a[100009],b[100009];
long long k,c[100009];//排名和下标为x的数为开头的LIS方案数
struct gzw
{
    long long mx,cnt;//LIS长度和方案数
    inline friend void operator += (gzw &a, const gzw &b)//状态转移
    {
        if (a.mx < b.mx) a.mx = b.mx, a.cnt = b.cnt;
        else if (a.mx == b.mx) a.cnt = min(inf, a.cnt + b.cnt);
    }
}bit[100009];
vector<long long> vec[100009];//LIS长度为x的所有开头的下标
bool use[100009];//x这个下标是否在LIS中
void add(long long x,gzw y)//更新
{
    for(long long i=x;i;i-=(i&-i))
    {
        bit[i]+=y;
    }
}
gzw query(long long x)//由于是从后往前做的,应当找权值比当前大的转移
{
    gzw re={0,1ll};
    for(long long i=x;i<=n;i+=(i&-i))
    {
        re+=bit[i];
    }
    return re;
}
int main()
{
    n=read(),k=read();
    //cin>>n>>k;
    for(long long i=1;i<=n;i++)
    {
        a[i]=read();
        //cin>>a[i];
        b[i]=a[i];
    }
    for(long long i=n;i;i--)
    {
        gzw ls=query(b[i]+1);
        ls.mx++;//更新后长度+1
        vec[ls.mx].push_back(i);//加入下标 因为i是从大到小枚举故必然有序
        c[i]=ls.cnt;
        add(b[i],ls);
    }
    m=query(1).mx;//查询最长子序列长度
    long long cur=-1;
    for(long long i=m;i;i--)
    {
        for(long long ii=0;ii<vec[i].size();ii++)
        {
            long long j=vec[i][ii]; //下标
            if(b[j]<cur) continue; //权值不能比之前的小
            if(k<=c[j])//往下走
            {
                use[b[j]]=1;
                cur=b[j];
                break;
            }
            k-=c[j];//再往后面找下标更小的    
        }
    }
    printf("%lld\n",n-m);
    for(long long i=1;i<=n;i++)
    {
        if(!use[i]) printf("%lld\n",i);    //输出答案
    }
    return 0;
}

花了一下午终于弄懂了QAQ,果然题解写的越复杂的人越菜QAQ


 上一篇
均分纸牌(luogu1031)糖果传递(luogu2512)七夕会(bzoj3032)三合一题解 均分纸牌(luogu1031)糖果传递(luogu2512)七夕会(bzoj3032)三合一题解
题意转换首先肯定将有摊位的位置和没摊位的位置进行交换才是又贡献的,所以将摊位看作1,实际上就是环状的均分纸牌的问题 性质其次行和列是可以分开处理的,对于行而言只需上下移动即可,对于列而言只需左右移动,故行列可分开处理 无解只需判断总摊位数能
2019-02-28
下一篇 
题解 P5155 【[USACO18DEC]Balance Beam】 题解 P5155 【[USACO18DEC]Balance Beam】
分步证明,首先抛出结论:每个点的策略要么是不动,要么是随机移动直到左右两个点中的一个落下。 结论1:从点x开始在a和b之间移动在b落下的概率为 (x-a)/(b-a)设概率为f[x]=k,则f[a]=0,f[b]=1。因为x有1/2几率
2019-02-22