[BZOJ2244][SDOI2011]拦截导弹
sol
某一枚导弹被拦截的概率等于这一枚导弹被拦截的方案数,也就是这枚导弹参与构成最长不上升子序列的方案数,除以最长不上升子序列的总方案数。总方案数会爆long long,但是可以用double存(雾)
从前往后做一遍最长不上升子序列,再从后往前做一遍最长不下降子序列,并且求方案数。一枚导弹被拦截的方案数等于前后的方案数的乘积。如果一枚导弹的前后\(dp\)值相加-1不等于LIS,那么这枚导弹就不可能出现在最长不上升子序列里面,因此答案为0。
树套树维护\(dp\)值同时统计方案数。
code
#include<cstdio>
#include<algorithm>
using namespace std;
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 5e4+5;
struct Data{
int f;double g;
Data operator + (const Data &b) const
{
if (f>b.f) return (Data){f,g};
if (f<b.f) return (Data){b.f,b.g};
return (Data){f,g+b.g};
}
}dp[2][N];
struct president_tree{int ls,rs;Data v;}t[N*150];
int n,a[N],b[N],o1[N],o2[N],len1,len2,rt[N],tot,ans;double cnt;
void modify(int &x,int l,int r,int p,Data v)
{
if (!x) x=++tot;
t[x].v=t[x].v+v;
if (l==r) return;int mid=l+r>>1;
if (p<=mid) modify(t[x].ls,l,mid,p,v);
else modify(t[x].rs,mid+1,r,p,v);
}
Data query(int x,int l,int r,int ql,int qr)
{
if (!x||l>=ql&&r<=qr) return t[x].v;
int mid=l+r>>1;
if (qr<=mid) return query(t[x].ls,l,mid,ql,qr);
if (ql>mid) return query(t[x].rs,mid+1,r,ql,qr);
return query(t[x].ls,l,mid,ql,qr)+query(t[x].rs,mid+1,r,ql,qr);
}
int main()
{
n=gi();
for (int i=1;i<=n;++i) o1[++len1]=a[i]=gi(),o2[++len2]=b[i]=gi();
sort(o1+1,o1+len1+1);len1=unique(o1+1,o1+len1+1)-o1-1;
sort(o2+1,o2+len2+1);len2=unique(o2+1,o2+len2+1)-o2-1;
for (int i=1;i<=n;++i)
{
a[i]=lower_bound(o1+1,o1+len1+1,a[i])-o1;
b[i]=lower_bound(o2+1,o2+len2+1,b[i])-o2;
}
for (int i=1;i<=n;++i)
{
a[i]=len1-a[i]+1;dp[0][i]=(Data){0,1};
for (int j=a[i];j;j-=j&-j)
dp[0][i]=dp[0][i]+query(rt[j],1,n,b[i],len2);
dp[0][i].f++;
for (int j=a[i];j<=len1;j+=j&-j)
modify(rt[j],1,n,b[i],dp[0][i]);
}
for (int i=1;i<=len1;++i) rt[i]=0;
for (int i=1;i<=tot;++i) t[i].ls=t[i].rs=0,t[i].v.f=0,t[i].v.g=0;tot=0;
for (int i=n;i;--i)
{
a[i]=len1-a[i]+1;dp[1][i]=(Data){0,1};
for (int j=a[i];j;j-=j&-j)
dp[1][i]=dp[1][i]+query(rt[j],1,n,1,b[i]);
dp[1][i].f++;
for (int j=a[i];j<=len1;j+=j&-j)
modify(rt[j],1,n,b[i],dp[1][i]);
}
for (int i=1;i<=n;++i) ans=max(ans,dp[0][i].f+dp[1][i].f-1);
printf("%d\n",ans);
for (int i=1;i<=n;++i) if (dp[0][i].f==ans) cnt+=dp[0][i].g;
for (int i=1;i<=n;++i)
if (ans==dp[0][i].f+dp[1][i].f-1)
printf("%.6lf ",dp[0][i].g*dp[1][i].g/cnt);
else printf("0.000000 ");
puts("");return 0;
}