2244: [SDOI2011]拦截导弹
Time Limit: 30 Sec Memory Limit: 512 MBSec Special JudgeSubmit: 662 Solved: 267
[ Submit][ Status][ Discuss]
Description
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度、并且能够拦截任意速度的导弹,但是以后每一发炮弹都不能高于前一发的高度,其拦截的导弹的飞行速度也不能大于前一发。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
在不能拦截所有的导弹的情况下,我们当然要选择使国家损失最小、也就是拦截导弹的数量最多的方案。但是拦截导弹数量的最多的方案有可能有多个,如果有多个最优方案,那么我们会随机选取一个作为最终的拦截导弹行动蓝图。
我方间谍已经获取了所有敌军导弹的高度和速度,你的任务是计算出在执行上述决策时,每枚导弹被拦截掉的概率。
Input
第一行包含一个正整数n,表示敌军导弹数量;
下面 行按顺序给出了敌军所有导弹信息:
第i+1行包含2个正整数hi和vi,分别表示第 枚导弹的高度和速度。
Output
输出包含两行。
第一行为一个正整数,表示最多能拦截掉的导弹数量;
第二行包含n个0到1之间的实数,第i个数字表示第i枚导弹被拦截掉的概率(你可以保留任意多位有效数字)。
Sample Input
3 30
4 40
6 60
3 30
Sample Output
0.33333 0.33333 0.33333 1.00000
【数据规模和约定】
对于100%的数据,1≤n≤5*104, 1≤hi ,vi≤109;
均匀分布着约30%的数据,所有vi均相等。
均匀分布着约50%的数据,满足1≤hi ,vi≤1000。
HINT
鸣谢kac提供sj程序!
Source
【分析】
比较难搞的CDQ分治...因为个人智障等原因从早上10:30写到下午16:20...卧槽6个小时(虽然中间午休了一会)
曾经的导弹拦截只用求最长上升子序列,相当于二维偏序,本题加了一维,所以三维偏序,我们要用CDQ降一维。
把整个序列翻转后,考虑二维偏序的最长上升子序列如何求出。CDQ分治中三维分别为id,x,y,id为导弹的先后顺序,为方便我们将x,y都离散化掉。用a[i].f表示以导弹i为结尾的LIS长度,a[i].g表示以导弹i为结尾在LIS的条件下总共有几种方案。我们在处理区间(l,r)时,要先以id排序,处理区间(l,mid),处理完之后将(l,mid)和(mid+1,r)以x为关键字排序,设置两个指针,用树状数组更新答案,注意这里的树状数组有一些特殊的地方,详见代码。
那么导弹i为结尾的LIS都处理出来了,还要处理导弹i为开头的。事实上很简单,只要把序列翻转回来,x=n+1-x,y=n+1-y,就可以调用之前的函数统计答案了。
第二问的答案显然是一颗导弹所在的lis数/总的lis数。用g数组求解即可。
【代码】
//bzoj 2244
#include<bits/stdc++.h>
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxn=50005;
int n,m,t1,t2,T,kd;
int h[mxn],s[mxn],tim[mxn],t_f[mxn];
double t_g[mxn],ans[mxn];
struct bullet
{
int id,x,y,f[2];double g[2];
}a[mxn],tmp[mxn];
inline bool comp(bullet u,bullet v)
{
if(u.x==v.x) return u.y<v.y;
return u.x<v.x;
}
inline bool cmp(bullet u,bullet v)
{
return u.id<v.id;
}
inline int lowbit(int x) {return x&-x;}
inline void add(int u,int f,double g)
{
for(int i=u;i<=n;i+=lowbit(i))
{
if(tim[i]!=T) tim[i]=T,t_f[i]=t_g[i]=0;
if(f>t_f[i]) t_f[i]=f,t_g[i]=g;
else if(f==t_f[i]) t_g[i]+=g;
}
}
inline void getsum(int u,int &len,double &calc)
{
len=0,calc=0.0;
for(int i=u;i>=1;i-=lowbit(i))
if(tim[i]==T)
{
if(t_f[i]>len) len=t_f[i],calc=t_g[i];
else if(t_f[i]==len) calc+=t_g[i];
}
}
inline void CDQ(int l,int r)
{
// if(l==5 && r==7 && kd==1)
// {
// puts("fuck");
// }
if(l==r)
{
if(!a[l].f[kd]) a[l].f[kd]=a[l].g[kd]=1;
return;
}
int i,j,mid=l+r>>1,l1=l,l2=mid+1;
sort(a+l,a+r+1,cmp);
CDQ(l,mid);T++;
sort(a+mid+1,a+r+1,comp);
for(l2=mid+1;l2<=r;l2++)
{
for(l1;l1<=mid&&a[l1].x<=a[l2].x;l1++)
add(a[l1].y,a[l1].f[kd],a[l1].g[kd]);
int mx;double sum;
getsum(a[l2].y,mx,sum);
if(mx>0)
{
if(mx+1>a[l2].f[kd])
a[l2].f[kd]=mx+1,a[l2].g[kd]=sum;
else if(mx+1==a[l2].f[kd])
a[l2].g[kd]+=sum;
}
}
CDQ(mid+1,r);
sort(a+l,a+r+1,comp);
// fo(i,1,n) printf("test=%d\n",a[i].id);
// puts("");
}
int main()
{
int i,j;
scanf("%d",&n);
for(i=n;i>=1;i--)
{
scanf("%d%d",&a[i].x,&a[i].y);
h[i]=a[i].x,s[i]=a[i].y,a[i].id=i;
}
sort(h+1,h+n+1),sort(s+1,s+n+1);
t1=unique(h+1,h+n+1)-h-1;
t2=unique(s+1,s+n+1)-s-1;
fo(i,1,n) a[i].x=lower_bound(h+1,h+t1+1,a[i].x)-h;
fo(i,1,n) a[i].y=lower_bound(s+1,s+t2+1,a[i].y)-s;
kd=0,CDQ(1,n);
sort(a+1,a+n+1,cmp);
reverse(a+1,a+n+1);
fo(i,1,n) a[i].x=n-a[i].x+1;
fo(i,1,n) a[i].y=n-a[i].y+1;
fo(i,1,n) a[i].id=i;
kd=1,CDQ(1,n);
//part one.
int mxlen=0;
double sum=0.0;
fo(i,1,n)
if(a[i].f[0]>mxlen)
mxlen=a[i].f[0],sum=a[i].g[0];
else if(a[i].f[0]==mxlen)
sum+=a[i].g[0];
// printf("test=%.5lf\n",sum);
printf("%d\n",mxlen);
fo(i,1,n) if(a[i].f[0]+a[i].f[1]-1==mxlen)
ans[a[i].id]=(a[i].g[0]*a[i].g[1])/sum;
fo(i,1,n-1) printf("%.5lf ",ans[i]);
printf("%.5lf\n",ans[n]);
return 0;
}
/*
7
2 5
3 6
2 5
2 4
3 5
1 9
6 3
*/