这是CDQ分治优化1D/1D动态规划的模板题(1D/1D动态规划的概念见OI-wiki)
一般来说,优化的1D/1D/动态规划,在转移的时候是由不等式作为条件的,所以可以像这样转化为三维偏序
用线段树进行如下维护:
1.维护区间最大值
2.查询区间最大值的某一数组的和
代码见下(一定要学会将数组翻转的操作)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e4+10;
const double eps=1e-5;
int n;
bool OP;
struct node
{
int h,v,t;
}m[N];
int Max[N<<2],f[N][2],cnt,v[N],e[N];
double sum[N<<2],g[N][2];
bool cmp(node i,node j)
{
return i.h>j.h;
}
void modify(int p,int l,int r,int x,int d,bool op,bool flag)
{
if(l>x||r<x) return;
if(l==r)
{
if(!flag)
{
Max[p]=-1,sum[p]=0;
return;
}
if(Max[p]<f[m[d].t][op])
{
Max[p]=f[m[d].t][op];
sum[p]=g[m[d].t][op];
}
else if(Max[p]==f[m[d].t][op]) sum[p]+=g[m[d].t][op];
return;
}
int mid=l+r>>1;
modify(p<<1,l,mid,x,d,op,flag);
modify(p<<1|1,mid+1,r,x,d,op,flag);
if(Max[p<<1]>Max[p<<1|1]) sum[p]=sum[p<<1];
else if(Max[p<<1]<Max[p<<1|1]) sum[p]=sum[p<<1|1];
else sum[p]=sum[p<<1]+sum[p<<1|1];
Max[p]=max(Max[p<<1],Max[p<<1|1]);
}
pair<int,double> ask(int p,int l,int r,int x,int y)
{
if(l>y||r<x) return make_pair(-1,0);
if(l>=x&&r<=y) return make_pair(Max[p],sum[p]);
int mid=l+r>>1;
pair<int,double> res1,res2;
res1=ask(p<<1,l,mid,x,y);
res2=ask(p<<1|1,mid+1,r,x,y);
if(res1.first>res2.first) return res1;
else if(res1.first<res2.first) return res2;
else return make_pair(res1.first,res1.second+res2.second);
}
bool cmp1(node i,node j)
{
if(!OP) return i.t<j.t;
else return i.t>j.t;
//这个排序想一下为什么这么写而不是直接写成i.t<j.t;
}
void CDQ(int l,int r,bool op)
{
if(l==r)
{
f[m[l].t][op]++;
if(f[m[l].t][op]==1) g[m[l].t][op]=1;//这个导弹作为第一发
return;
}
int mid=l+r>>1;
CDQ(l,mid,op);
sort(m+l,m+mid+1,cmp);
sort(m+mid+1,m+r+1,cmp);
for(int i=l,j=mid+1;j<=r;j++)
{
while(i<=mid&&m[i].h>=m[j].h)
{
modify(1,1,cnt,lower_bound(e+1,e+cnt+1,m[i].v)-e,i,op,1);
//最后一个参数为1表示更新,为0表示还原
i++;
}
pair<int,double> nowmax=ask(1,1,cnt,lower_bound(e+1,e+cnt+1,m[j].v)-e,cnt);
if(f[m[j].t][op]<nowmax.first)
{
f[m[j].t][op]=nowmax.first;
g[m[j].t][op]=nowmax.second;
}
else if(f[m[j].t][op]==nowmax.first) g[m[j].t][op]+=nowmax.second;
}
for(int i=l,j=mid+1;j<=r;j++)
while(i<=mid&&m[i].h>=m[j].h)
{
modify(1,1,cnt,lower_bound(e+1,e+cnt+1,m[i].v)-e,i,op,0);
//还原
i++;
}
sort(m+mid+1,m+r+1,cmp1);//这个排序别忘了
CDQ(mid+1,r,op);
sort(m+l,m+r+1,cmp1);//这个排序也别忘了
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&m[i].h,&m[i].v);
m[i].t=i;
v[i]=m[i].v;
}
sort(v+1,v+n+1);
for(int i=1;i<=n;i++)
if(v[i]!=v[i-1]) e[++cnt]=v[i];
OP=0;
CDQ(1,n,0);
for(int i=1;i<=n;i++) m[i].h=-m[i].h,m[i].v=-m[i].v;
reverse(m+1,m+n+1);
for(int i=1;i<=cnt;i++) e[i]=-e[i];
sort(e+1,e+cnt+1);//上面这波操作很帅,学会
OP=1;
CDQ(1,n,1);
int maxans=-1;
for(int i=1;i<=n;i++)
maxans=max(maxans,f[i][0]);
printf("%d\n",maxans);
double sum=0;
for(int i=1;i<=n;i++)
if(f[i][0]==maxans) sum+=g[i][0];
for(int i=1;i<=n;i++)
if(f[i][0]+f[i][1]-1==maxans) printf("%.7lf ",g[i][0]/sum*g[i][1]);
else printf("0 ");
return 0;
}