1. 程式人生 > >[TJOI2014]Alice and Bob[拓撲排序+貪心]

[TJOI2014]Alice and Bob[拓撲排序+貪心]

pen arrow 如果 pri 說我 getc ret get bool

題意

給出一個序列的以每一項結尾的 \(LIS\) 的長度a[],求一個序列,使得以每一項為開頭的最長下降子序列的長度之和最大。

\(n\leq 10^5\)

分析

  • 最優解一定是一個排列,因為如果兩個數字的大小相同,完全可以區別他們的大小,以得到更多的貢獻。

  • 考慮的 \(a\) 給定的限制,顯然對於所有的相同大小的 \(a\) ,前一項 \(a_{p_1}\) 要大於後一項 \(a_{p_2}\),否則一定會產生更長的上升子序列。連邊\(p_2\rightarrow p_1\)表示 \(p_2\) 的值小於\(p_1\)

  • 對於 \(i\),找到上一次出現 \(a[i]-1\) 的位置,並連邊 $ {pos}_{a[i]-1}\rightarrow i$ ,表示 \(i\)

    要大於此位置的值。

  • 然後進行貪心的操作,每次在所有入度為0的點中選擇編號最大的並賦上最小的權值。確定數值之後依次確定 \(b\) 的值就好了。

  • 正確性顯然,因為對於相同的 \(a_x\) 來說我們會優先考慮最靠後的 \(last\),同時靠前的不會向 \(last\) 後邊連邊。

  • 總時間復雜度為 \(O(nlogn)\) ,主要是 \(BIT\) 的復雜度。

代碼

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cctype>
#include<queue>
#include<vector>
using namespace std;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define go(u) for(int i=head[u],v=e[i].to;i;i=e[i].last,v=e[i].to)
typedef long long LL;
typedef pair<int,int> pii;
inline int gi(){
  int x=0,f=1;char ch=getchar();
  while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
  while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
  return x*f;
}
template<typename T>inline bool Max(T &a,T b){return a<b?a=b,1:0;}
template<typename T>inline bool Min(T &a,T b){return a>b?a=b,1:0;}
const int N=1e5 + 7;
int n,edc;
int head[N],ind[N],lst[N],a[N],b[N],f[N];
struct edge{
    int last,to;
    edge(){}edge(int last,int to):last(last),to(to){}
}e[N*2];
void Add(int a,int b){
    e[++edc]=edge(head[a],b),head[a]=edc;
    ++ind[b];
}
int tr[N];
int lowbit(int x){return x&-x;}
void modify(int x,int y){for(int i=x;i<=n;i+=lowbit(i)) Max(tr[i],y);}
int query(int x){int res=0;for(int i=x;i;i-=lowbit(i)) Max(res,tr[i]);return res;}
priority_queue<int>Q;
void topo(){
    rep(i,1,n) if(!ind[i]) Q.push(i);int tmp=0;
    while(!Q.empty()){
        int u=Q.top();Q.pop();
        b[u]=++tmp;
        go(u)
            if(--ind[v]==0) Q.push(v);
    }
}
int main(){
    n=gi();
    rep(i,1,n){
        a[i]=gi();
        if(lst[a[i]]) Add(i,lst[a[i]]);
        if(lst[a[i]-1]) Add(lst[a[i]-1],i);
        lst[a[i]]=i;
    }
    topo();
    LL ans=0;
    for(int i=n;i;--i){
        f[i]=query(b[i]-1)+1;
        modify(b[i],f[i]);
        ans+=f[i];
    }
    printf("%lld\n",ans);
    return 0;
}

[TJOI2014]Alice and Bob[拓撲排序+貪心]