洛谷P2487 [SDOI2011]攔截導彈(cdq分治+dp)
題目鏈接:傳送門
思路:
這個其實就是求三維偏序的最長子序列,且求出每個三元組在所有最長子序列中的出現次數。其中第一維是導彈出現的順序。
我們先寫下dp方程,爲第 i 個元素結尾的最長子序列的長度,爲第 i 個元素結尾的最長子序列的方法數。容易寫出dp方程,其中。
對於區間,設。那麼對於i ,j的轉移不外乎三種情況:
另外總所周知,dp順序非常重要。
所以我們先計算第一種情況,這個可以使用自身的函數。
對於第二種轉移,我們考慮所有右區間的i,將所有滿足的加入樹狀數組中。並維護對應的最大長度和方法數,對於每個,我們找的最大長度,並得到該最大長度的種類數。使用雙指針實現所有右區間的。
然後計算第三種情況,因爲此時右區間的已經不是按照a的大小排列了,但該分治必須要求a有序,所以我們還需大力sort回來。
總的時間複雜度爲。
那麼所有的最長子序列的種類就是滿足的所有之和。
根據最終的所有,我們可以求出三維偏序最長子序列的長度。
我們再按照同一種方法計算和,代表以第個元組開始的三維偏序的最長子序列的長度和方法數。
如果,那就證明該元組在其中一個最長子序列中,其出現的次數爲。
注意:
- 種類數會爆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̲