洛谷P2487 [SDOI2011]攔截導彈(cdq分治+dp)

洛谷P2487 [SDOI2011]攔截導彈(cdq分治+dp)

題目鏈接傳送門

思路

​ 這個其實就是求三維偏序的最長子序列,且求出每個三元組在所有最長子序列中的出現次數。其中第一維是導彈出現的順序。

我們先寫下dp方程,fls[i]fls[i]爲第 i 個元素結尾的最長子序列的長度,fkind[i]fkind[i]爲第 i 個元素結尾的最長子序列的方法數。容易寫出dp方程fls[i]=max(fls[i],fls[j]+1)fls[i]=max (fls[i],fls[j]+1),其中{ji , bjbi , cjci}\{j\le i ~,~b_j\le b_i~, ~c_j\le c_i\}

對於區間(l,r)(l,r),設m=(l+r)/2m=(l+r)/2。那麼對於i ,j的轉移不外乎三種情況:

  • j(l,m),i(l,m)j\in(l,m),i\in(l,m)​
  • j(l,m),i(m+1,r)j\in(l,m),i\in(m+1,r)​
  • j(m+1,r),i(m+1,r)j\in(m+1,r),i\in(m+1,r)

另外總所周知,dp順序非常重要。

所以我們先計算第一種情況,這個可以使用自身的函數cdq(l,m)cdq(l,m)

對於第二種轉移,我們考慮所有右區間的i,將所有滿足bj<=bib_j<=b_icjc_j加入樹狀數組中。並維護對應的最大長度和方法數,對於每個ii,我們找ci\le c_i的最大長度,並得到該最大長度的種類數。使用雙指針實現所有右區間的ii

然後計算第三種情況,因爲此時右區間的已經不是按照a的大小排列了,但該分治必須要求a有序,所以我們還需大力sort回來。

總的時間複雜度爲O(n logn logn)O(n~logn~logn)​

那麼所有的最長子序列的種類就是滿足fls[i]==maxlisfls[i]==maxlis的所有fkind[i]fkind[i]之和。

根據最終的所有fls[i]fls[i],我們可以求出三維偏序最長子序列的長度。

我們再按照同一種方法計算gls[i]gls[i]gkind[i]gkind[i],代表以第ii個元組開始的三維偏序的最長子序列的長度和方法數。

如果gls[i]+fls[i]1==maxlisgls[i]+fls[i]-1==maxlis,那就證明該元組在其中一個最長子序列中,其出現的次數爲fkind[i]gkind[i]fkind[i]*gkind[i]

注意:

  • 種類數會爆long long ,但因爲最後求得是分數,所以我們可以用double 儲存。
  • 樹狀數組可以維護每個區間相同最大值的方法數.(剛開始以爲無法實現,sb了)

我太難了-- 2019-9-12 12:10------- 2019-9-13 14:14

#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int N=1e5+20;
int MXN;
struct TreeArray
{
    int mxbt[N];
    double kdbt[N];//初始化都爲0
    int lowbit(int k)
    {
        return k&-k;
    }
    void updatemax(int k,int val,double kinds)//種類數
    {
        while(k<=MXN)
        {
            if(mxbt[k] < val)
            {
                mxbt[k]=val;
                kdbt[k]=kinds;
            }
            else if(mxbt[k] == val)
            {
                kdbt[k]+=kinds;
            }
            k+=lowbit(k);
        }
    }
    int getpremax(int k,double &kinds)//返回mx<=k的最大長度和對應的種類數
    {
        int ans=-1;
        kinds=0;
        while(k)
        {
            if(mxbt[k] > ans)
            {
                ans=mxbt[k];
                kinds=kdbt[k];
            }
            else if(mxbt[k]==ans)
                kinds+=kdbt[k];
            k-=lowbit(k);
        }
        return ans;
    }
    void en(int k)//add k的逆過程
    {
        while(k<=MXN)
        {
            mxbt[k]=0;
            kdbt[k]=0;
            k+=lowbit(k);
        }
    }
} hd;
struct node
{
    int a,b,c,fls,gls;//該節點爲結尾的偏序長度
    double fkd,gkd;
} t[N];
int n;
bool cmpa(const node& a,const node & b)
{
    if(a.a!=b.a)return a.a<b.a;
    return a.c<b.c;
}
bool gecmpa(const node& a,const node & b)
{
    if(a.a!=b.a) return a.a>b.a;
    return a.c>b.c;
}
bool cmpb(const node &a,const node & b)
{
    return a.b<b.b;
}
bool gecmpb(const node &a,const node & b)
{
    return a.b>b.b;
}
bool cmpc(const node &a,const node & b)
{
    return a.c<b.c;
}
void cdq1(int l,int r)
{
//    printf("l:%d,r:%d\n",l,r);
    if(r==l) return ;
    int m=(l+r)>>1;
    cdq1(l,m);
    sort(t+l,t+m+1,cmpb);
    sort(t+m+1,t+r+1,cmpb);
    int p=l-1;
    for(int i=m+1; i<=r; ++i)
    {
        while(p+1<=m&&t[p+1].b<=t[i].b)
        {
            ++p;
            hd.updatemax(t[p].c,t[p].fls,t[p].fkd);
        }
        int fls;
        double fkd;
        fls=hd.getpremax(t[i].c,fkd);
        if(fls==0) continue;
        if(fls+1 > t[i].fls)
        {
            t[i].fls=fls+1;
            t[i].fkd=fkd;
        }
        else if(fls+1 == t[i].fls)
            t[i].fkd+=fkd;
    }
    for(int i=l; i<=p; ++i)
        hd.en(t[i].c);
    sort(t+m+1,t+r+1,cmpa);
    cdq1(m+1,r);
}
void cdq2(int l,int r)
{
    if(r==l) return ;
    int m=(l+r)>>1;
    cdq2(l,m);
    sort(t+l,t+m+1,gecmpb);
    sort(t+m+1,t+r+1,gecmpb);
    int p=l-1;
    for(int i=m+1; i<=r; ++i)
    {
        while(p+1<=m&&t[p+1].b>=t[i].b)
        {
            ++p;
            hd.updatemax(n-t[p].c+1,t[p].gls,t[p].gkd);
        }
        int gls;
        double gkd;
        gls=hd.getpremax(n-t[i].c+1,gkd);
        if(gls==0) continue;
        if(gls+1 > t[i].gls)
        {
            t[i].gls=gls+1;
            t[i].gkd=gkd;
        }
        else if(gls+1 == t[i].gls)
            t[i].gkd+=gkd;
    }
    for(int i=l; i<=p; ++i)
        hd.en(n-t[i].c+1);
    sort(t+m+1,t+r+1,gecmpa);
    cdq2(m+1,r);
}
int main()
{
    scanf("%d",&n);
    MXN=n;
    for(int i=1; i<=n; ++i)
    {
        int h,v;
        scanf("%d%d",&h,&v);
        t[i].a=-h;
        t[i].b=-v;
        t[i].c=i;
        t[i].fls=1;
        t[i].fkd=1.0;
        t[i].gls=1;
        t[i].gkd=1.0;
    }//h爲第一維度
    sort(t+1,t+n+1,cmpa);
    cdq1(1,n);
    int mxlis=-1;
    for(int i=1; i<=n; ++i)
        mxlis=max(mxlis,t[i].fls);
    sort(t+1,t+n+1,gecmpa);
    cdq2(1,n);
    printf("%d\n",mxlis);
    double sum=0;
    for(int i=1;i<=n;++i)
        if(t[i].fls==mxlis) sum+=t[i].fkd;
    sort(t+1,t+1+n,cmpc);
    for(int i=1; i<=n; ++i)
    {
        if(t[i].fls+t[i].gls-1==mxlis)
        {
            printf("%.5f ",t[i].fkd*t[i].gkd/sum);
        }
        else printf("0.00000 ");
    }
}
  • KaTeX parse error: Can't use function '\r' in math mode at position 2: i\̲r̲
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章