zoukankan      html  css  js  c++  java
  • [USACO18DEC]Sort It Out P

    LXXIV.[USACO18DEC]Sort It Out P

    集合中的数一定是某一条LIS的补集,这点还是比较好想的。

    我们要集合的字典序最小,就是让集合的补集的字典序最大。

    最大就可以考虑按位处理LIS中的数。

    我们从后往前求LIS。我们设\(f[i]\)表示以当前位置开头的LIS的长度以及数量(类型是一个pair)。\(f[i]\)可以直接套BIT解决。

    然后,对于每个\(k\),我们将所有长度为\(k\)的位置丢到一个vector里面,即为确定序列正数第\(k\)时的过程;然后从小往大遍历这些位置。就是正常的求第\(k\)大的常规思路。

    注意,因为是LIS,当你将一个数选入序列后,所有比它小的位置都不能再选了!!!

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define pii pair<int,ll>
    #define X first
    #define Y second
    #define mp make_pair
    #define O mp(0,0)
    const ll lim=1e18;
    int n,a[100100],len;
    bool on[100100];
    pii f[100100],t[100100];
    void operator +=(pii &x,const pii &y){
    	if(x.X<y.X)x=y;
    	else if(x.X==y.X)x.Y=min(lim,x.Y+y.Y);
    }
    void ADD(int x,pii y){while(x)t[x]+=y,x-=x&-x;}
    pii ASK(int x){pii ret=O;while(x<=n)ret+=t[x],x+=x&-x;return ret;}
    ll m;
    vector<int>v[100100];
    int main(){
    	scanf("%d%lld",&n,&m);
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    	ADD(n,mp(0,1));
    	for(int i=n;i;i--)f[i]=ASK(a[i]),f[i].X++,ADD(a[i],f[i]),len=max(len,f[i].X),v[f[i].X].push_back(i);
    	printf("%d\n",n-len);
    	for(int i=len,k=1;i;i--){
    		reverse(v[i].begin(),v[i].end());
    		for(auto j:v[i]){
    			if(f[j].Y<m)m-=f[j].Y;
    			else{
    				on[a[j]]=true;
    				while(k<j)f[k++]=O;
    				break;
    			}
    		}
    	}
    	for(int i=1;i<=n;i++)if(!on[i])printf("%d\n",i);
    	return 0;
    }
    

  • 相关阅读:
    0100相同的树 Marathon
    0017电话号码的字母组合 Marathon
    0236二叉树的最近公共祖先 Marathon
    1365有多少小于当前数字的数字 Marathon
    0701二叉搜索树的插入操作 Marathon
    0538二叉搜索树转为累加树 Marathon
    0039组合总和 Marathon
    0098验证二叉搜索树 Marathon
    0700二叉搜索树中的搜索 Marathon
    0108有序数组转为二叉搜索树 Marathon
  • 原文地址:https://www.cnblogs.com/Troverld/p/14598405.html
Copyright © 2011-2022 走看看