[CF340D]Bubble Sort Graph/[JZOJ3485]独立集

题目大意:
  给你一个序列,对序列中所有逆序对之间连一条边,问图中最大独立集为多大,有哪些点一定在最大独立集中。

思路:
  在纸上画一下发现最大独立集一定是元序列的一个LIS,最大独立集必经点就是所有LIS的公共部分。
  考虑把所有的LIS记录下来,然后构建一个DAG,DAG的割点即为LIS的公共部分。
  由于我们需要先知道所有的LIS的具体方案,只能写O(n^2)的DP记录转移,能拿60分。
  不过事实上我们也不需要知道具体的状态。
  我们可以记录一下所有LIS中的每个点在LIS上的哪个位置,看一下这个位置是否只有它一个点的。
  如果不,那么肯定可以被别的点替代。
  如果是,那么它肯定是LIS的公共部分。
  LIS可以O(n log n)求,最后判断是O(n)的,总的时间复杂度是O(n log n)。

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x; 
}
const int N=100001;
int w[N],id[N],f[N],g[N],h[N];
int n;
class FenwickTree {
    private:
        int val[N];
        int lowbit(const int &x) const {
            return x&-x;
        } 
    public:
        void reset() {
            memset(val,0,sizeof val);
        }
        void modify(int p,const int &x) {
            while(p<=n) {
                val[p]=std::max(val[p],x);
                p+=lowbit(p);
            }
        }
        int query(int p) const {
            int ret=0;
            while(p) {
                ret=std::max(ret,val[p]);
                p-=lowbit(p);
            }
            return ret;
        }
};
FenwickTree t;
int main() {
    n=getint();
    for(register int i=1;i<=n;i++) {
        const int v=getint();
        w[i]=v;
        id[v]=i;
    }
    for(register int i=1;i<=n;i++) {
        f[w[i]]=t.query(w[i])+1;
        t.modify(w[i],f[w[i]]);
    }
    t.reset();
    for(register int i=n;i;i--) {
        g[w[i]]=t.query(n-w[i]+1)+1;
        t.modify(n-w[i]+1,g[w[i]]);
    }
    int ans=0;
    for(register int i=1;i<=n;i++) {
        ans=std::max(ans,f[w[i]]+g[w[i]]-1);
    }
    printf("%d\n",ans);
    for(register int i=1;i<=n;i++) {
        if(f[w[i]]+g[w[i]]-1==ans) {
            if(!~h[f[w[i]]]) continue;
            if(!h[f[w[i]]]) {
                h[f[w[i]]]=1;
            } else {
                h[f[w[i]]]=-1;
            }
        }
    }
    for(register int i=1;i<=n;i++) {
        if(f[w[i]]+g[w[i]]-1==ans&&h[f[w[i]]]==1) {
            printf("%d ",i);
        }
    }
    return 0;
}

 

posted @ 2017-11-01 11:06  skylee03  阅读(115)  评论(0编辑  收藏  举报