BZOJ 2244: [SDOI2011]拦截导弹(三维偏序+BIT)

博客介绍了一道题的解题思路,对于第一问,可对时间进行CDQ分治,按高度排序,用BIT维护速度前缀最大值来计算;对于第二问,指出BIT在维护前缀最大值时还能维护方案数,最后给出了AC代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

显然可以对时间CDQ分治,然后按高度排序,对速度用BIT维护前缀最大值。
这样就可以算第一问。
对于第二问。
思维不要僵化。
BIT可以在维护前缀最大值的同时维护方案数的。
额。。。
AC Code:

#include<bits/stdc++.h>
#define maxn 100005
using namespace std;

int n,h[maxn],v[maxn],sh[maxn],sv[maxn],c[maxn];
int f[maxn],rf[maxn];
double g[maxn],rg[maxn];
bool cmp(const int &u,const int &v){ return h[u] == h[v] ? u < v : h[u] > h[v]; }

int Max[maxn],del[maxn],tp;
double Cnt[maxn];
void upd(int now,int val,double ways){ 
	for(;now<=sv[0];now+=now&-now){
		if(Max[now] == 0) del[tp++] = now;
		if(val>Max[now]) Max[now]=val,Cnt[now]=ways;
		else if(Max[now] == val) Cnt[now] += ways;
	}
}
pair<int,double> qsum(int now){
	pair<int,double>ret=make_pair(0,0);
	for(;now;now-=now&-now)
		if(ret.first < Max[now]) ret.first=Max[now],ret.second=Cnt[now];
		else if(ret.first == Max[now]) ret.second += Cnt[now];
	return ret;
}
	
void Solve(int l,int r,int *f,double *g){
	if(l>=r){ if(!f[l]) f[l]=1,g[l]=1;return; }
	int mid = (l+r) >> 1;
	Solve(l,mid,f,g);
	sort(c+l,c+mid+1,cmp);
	sort(c+mid+1,c+r+1,cmp);
	int j=l;
	//printf("@%d %d %d\n",l,mid,r);
	//for(int i=l;i<=r;i++) printf("%d\n",c[i]);
	for(int i=mid+1;i<=r;i++){
		for(;j<=mid && h[c[j]] >= h[c[i]];j++) {
			//printf("%d %d %d\n",c[j],v[c[j]],f[c[j]]);
			//printf("%d %d %.3lf\n",c[j],f[c[j]],g[c[j]]);
			upd(v[c[j]],f[c[j]],g[c[j]]);
		}
		//printf("%d %d\n",c[i],v[c[i]]);
		pair<int,double>ret=qsum(v[c[i]]);
		//printf("%d $%.3lf\n",ret.first,ret.second);
		//printf("%d %.3lf\n",f[2],g[2]);
		if(!ret.second) continue;
		if(ret.first+1>f[c[i]]) f[c[i]]=ret.first+1,g[c[i]]=ret.second;
		else if(ret.first+1==f[c[i]]) g[c[i]]+=ret.second;
		//printf("%d %.3lf\n",f[2],g[2]);
	}
	sort(c+mid+1,c+r+1);
	for(;tp;) Max[del[--tp]]=0,Cnt[del[tp]]=0;
	Solve(mid+1,r,f,g);
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d%d",&h[i],&v[i]),sh[++sh[0]]=h[i],sv[++sv[0]]=v[i],c[i]=i;
	sort(sh+1,sh+1+sh[0]),sort(sv+1,sv+1+sv[0]);
	sh[0]=unique(sh+1,sh+1+sh[0])-sh-1;
	sv[0]=unique(sv+1,sv+1+sv[0])-sv-1;
	for(int i=1;i<=n;i++) 
		h[i] = lower_bound(sh+1,sh+1+sh[0],h[i])-sh,
		v[i] = lower_bound(sv+1,sv+1+sv[0],v[i])-sv,
		v[i] = sv[0] + 1 - v[i];
	Solve(1,n,f,g);
	for(int i=1;i<=n;i++)
		v[i] = sv[0] + 1 - v[i],
		h[i] = sh[0] + 1 - h[i];
	for(int i=1;i<n-i+1;i++)
		swap(v[i],v[n-i+1]),
		swap(h[i],h[n-i+1]);
	//for(int i=1;i<=n;i++)
	//	printf("%d %d\n",h[i],v[i]);
	sort(c+1,c+1+n);
	Solve(1,n,rf,rg);
	for(int i=1;i<n-i+1;i++)
		swap(rf[i],rf[n-i+1]),
		swap(rg[i],rg[n-i+1]);
	//for(int i=1;i<=n;i++) printf("%.3lf\n",g[i]);
	int Max = *max_element(f+1,f+1+n);
	printf("%d\n",Max);
	double sum = 0;
	for(int i=1;i<=n;i++)
		if(f[i] == Max)
			sum += g[i];
	for(int i=1;i<=n;i++){
		//printf("%.3lf %.3lf %.3lf\n",g[i],rg[i],sum);
		printf("%.5lf%c",(f[i] + rf[i] == Max + 1) * g[i] * rg[i] / sum,i==n?'\n':' ');
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值