[SDOI2011] 拦截导弹

这是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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值