题意
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度、并且能够拦截任意速度的导弹,但是以后每一发炮弹都不能高于前一发的高度,其拦截的导弹的飞行速度也不能大于前一发。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
在不能拦截所有的导弹的情况下,我们当然要选择使国家损失最小、也就是拦截导弹的数量最多的方案。但是拦截导弹数量的最多的方案有可能有多个,如果有多个最优方案,那么我们会随机选取一个作为最终的拦截导弹行动蓝图。
我方间谍已经获取了所有敌军导弹的高度和速度,你的任务是计算出在执行上述决策时,每枚导弹被拦截掉的概率。
题解
很明显的一个CDQ分治DP。。
f[i]表示以他结尾的最多可以拦截多少个
这个用一个CDQ分治很快就可以算出来了
但是有多少种方案怎么弄呢?
其实我们可以在作CDQ的时候顺便弄出一个东西,就是这个答案优多少种方案
这个在树状数组上面加多一个辅助数组就可以了。。
然后经过他的怎么算呢?
我们可以倒着再做一个,求出后面开始,以他为开头的,其他一样,看做g[i]
如果一个导弹,
f[i]+g[i]−1=ans
,那么说明他是在上面的,用哪个辅助的数组统计答案就可以了
需要注意的地方:
1.在分治的时候,注意id的排序,因为我是分开的,前做左边,然后合并最后右边,所以会出现右边的id不顺序的情况
2.方案数乘积会爆
longlong
,要用
double
CODE:(调试都没有删。。)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
typedef long double LD;
const LL N=50005*2;
LL n;
struct qq
{
LL h,v,id;
}s[N],k[N];
LD f[N],h[N];
LD F[N],H[N];//对应的方案数
bool cmph (qq a,qq b){return a.h<b.h;}
bool cmpv (qq a,qq b){return a.v<b.v;}
bool cmpi (qq a,qq b){return a.id<b.id;}
LD g[N],G[N];
LL sta[N],top=0;
void LSH()
{
LL Last,Cnt;
for (LL u=1;u<=n;u++) k[u]=s[u];
sort(k+1,k+1+n,cmph);
Last=0;Cnt=0;
for (LL u=n;u>=1;u--)
{
if (k[u].h!=Last)
{
Last=k[u].h;
Cnt++;
}
s[k[u].id].h=Cnt;
}
for (LL u=1;u<=n;u++) k[u]=s[u];
sort(k+1,k+1+n,cmpv);
Last=0;Cnt=0;
for (LL u=n;u>=1;u--)
{
if (k[u].v!=Last)
{
Last=k[u].v;
Cnt++;
}
s[k[u].id].v=Cnt;
}
}
LL lb (LL x){return x&(-x);}
LD shen;//个数
LL get (LL x)
{
LD lalal=0;
while (x>=1)
{
if (g[x]>lalal) lalal=g[x],shen=0;
if (g[x]==lalal) shen=shen+G[x];
x-=lb(x);
}
return lalal;
}
void change (LL x,LL y,LD z)
{
while (x<=n)
{
if (g[x]<y) g[x]=y,G[x]=0;
if (g[x]==y) G[x]+=z;
x+=lb(x);
}
}
void reset (LL x)//这个位置清空
{
while (x<=n)
{
g[x]=0;G[x]=0;
x+=lb(x);
}
}
void cdq (LL l,LL r)
{
if (l==r) return ;
/* printf("OZY:%lld %lld\n",l,r);
for (LL u=l;u<=r;u++) printf("%lld ",s[u].id);
system("pause");*/
LL mid=(l+r)>>1;
sort(s+l,s+r+1,cmpi);
cdq(l,mid);
sort(s+l,s+1+mid,cmph);
sort(s+1+mid,s+r+1,cmph);
LL now=l;
// memset(g,0,sizeof(g));
// memset(G,0,sizeof(G));
top=0;
for (LL u=mid+1;u<=r;u++)//求解
{
while (now<=mid&&s[now].h<=s[u].h)
{
sta[++top]=s[now].v;
change(s[now].v,f[s[now].id],F[s[now].id]);
now++;
}
shen=0;
LL lalal=get(s[u].v)+1;
if (f[s[u].id]<lalal) f[s[u].id]=lalal,F[s[u].id]=0;
if (f[s[u].id]==lalal) F[s[u].id]+=shen;
}
for (LL u=1;u<=top;u++) reset(sta[u]);
cdq(mid+1,r);
/* for (LL u=1;u<=n;u++) printf("%lld ",f[u]);
printf("\n");*/
return ;
}
void cdq1 (LL l,LL r)
{
if (l==r) return ;
/* printf("OZY:%lld %lld\n",l,r);
for (LL u=l;u<=r;u++) printf("%lld ",s[u].id);
system("pause");*/
LL mid=(l+r)>>1;
sort(s+l,s+r+1,cmpi);
cdq1(mid+1,r);
sort(s+l,s+1+mid,cmph);sort(s+1+mid,s+r+1,cmph);
LL now=mid+1;
// memset(g,0,sizeof(g));
// memset(G,0,sizeof(G));
top=0;
for (LL u=l;u<=mid;u++)//求解?
{
while (now<=r&&s[now].h<=s[u].h)
{
sta[++top]=s[now].v;
change(s[now].v,h[s[now].id],H[s[now].id]);
now++;
}
shen=0;
LL lalal=get(s[u].v)+1;
if (h[s[u].id]<lalal) h[s[u].id]=lalal,H[s[u].id]=0;
if (h[s[u].id]==lalal) H[s[u].id]+=shen;
}
for (LL u=1;u<=top;u++) reset(sta[u]);
cdq1(l,mid);
return ;
}
int main()
{
scanf("%lld",&n);
for (LL u=1;u<=n;u++)
{
scanf("%lld%lld",&s[u].h,&s[u].v);
s[u].id=u;
f[u]=1;F[u]=1;H[u]=1;h[u]=1;
}
LSH();
sort(s+1,s+1+n,cmpi);
cdq(1,n);
LD ans=0;
for (LL u=1;u<=n;u++) ans=max(ans,f[u]);
/* for (LL u=1;u<=n;u++)
{
printf("%lld:%lld %lld\n",u,f[u],F[u]);
}
printf("\n");*/
printf("%.0Lf\n",ans);
sort(s+1,s+1+n,cmpi);
LSH();
sort(s+1,s+1+n,cmpi);
cdq1(1,n);
LD tot=0;//一共有多少种方案
for (LL u=1;u<=n;u++)
if (f[u]==ans)
tot=tot+F[u];
// printf("%lld\n",tot);
for (LL u=1;u<=n;u++)
{
if (f[u]+h[u]-1==ans)
{
printf("%.5Lf ",F[u]/tot*H[u]);
}
else printf("0.00000 ");
}
/* printf("\n");
printf("f:");
for (LL u=1;u<=n;u++) printf("%lld ",f[u]);
/* printf("\nF:");
for (LL u=1;u<=n;u++) printf("%lld ",F[u]);*/
/* printf("\nh:");
for (LL u=1;u<=n;u++) printf("%lld ",h[u]);
/* printf("\nH:");
for (LL u=1;u<=n;u++) printf("%lld ",H[u]);*/
return 0;
}