BZOJ 2244 SDOI2011 拦截导弹 CDQ分治/二维树状数组

题目大意:给定一个序列,每个元素是一个二元组,等概率选择一LIS,求LIS长度以及每个元素被选中的概率

第一问CDQ分治裸上

第二问用每个元素所在的LIS个数/总LIS个数就是答案

每个元素所在的LIS自己必选,然后统计前面的方案数和后面的方案数

以前面的方案数为例,令f[x]为以x结尾的LIS长度,那么有DP方程:

g[i]=Σg[j] (f[j]+1=f[i],j<i,a[j].x<a[i].x,a[j].y<a[i].y)

将所有元素按f值排序,分层DP,每层DP是一个三维偏序,上CDQ分治再搞搞就好了

(我偷懒写了二维树状数组QAQ)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 50500
using namespace std;
typedef double ld;
int n,ans,tot;
int f[M];
ld g[M],h[M],total;
struct abcd{
	int x,y;
}*a[M],*_a[M],mempool[M];
bool Compare(abcd *a1,abcd *a2)
{
	if( a1->x != a2->x )
		return a1->x < a2->x ;
	return a1->y < a2->y ;
}
bool _Compare(int x,int y)
{
	if( f[x] != f[y] )
		return f[x] < f[y] ;
	return x < y ;
}
void Discretization()
{
	static pair<int,int*> b[M];
	int i;
	for(i=1;i<=n;i++)
		b[i]=make_pair(-a[i]->x,&a[i]->x);
	sort(b+1,b+n+1);
	for(tot=0,i=1;i<=n;i++)
	{
		if(i==1||b[i].first!=b[i-1].first)
			++tot;
		*b[i].second=tot;
	}

	for(i=1;i<=n;i++)
		b[i]=make_pair(-a[i]->y,&a[i]->y);
	sort(b+1,b+n+1);
	for(tot=0,i=1;i<=n;i++)
	{
		if(i==1||b[i].first!=b[i-1].first)
			++tot;
		*b[i].second=tot;
	}
}
namespace BIT{
	int c[M],tim[M],T;
	void Initialize()
	{
		++T;
	}
	void Update(int x,int y)
	{
		for(;x<=n;x+=x&-x)
		{
			if(tim[x]!=T)
				tim[x]=T,c[x]=0;
			c[x]=max(c[x],y);
		}
	}
	int Get_Ans(int x)
	{
		int re=0;
		for(;x;x-=x&-x)
			if(tim[x]==T)
				re=max(re,c[x]);
		return re;
	}
}
void CDQ_Divide_And_Conquer(int l,int r)
{
	using namespace BIT;
	int i,j,mid=l+r>>1;
	if(l==r)
	{
		f[mid]++;
		ans=max(ans,f[mid]);
		return ;
	}
	int l1=l,l2=mid+1;
	for(i=l;i<=r;i++)
		if(a[i]-mempool<=mid)
			_a[l1++]=a[i];
		else
			_a[l2++]=a[i];
	memcpy(a+l,_a+l,sizeof(a[0])*(r-l+1));
	CDQ_Divide_And_Conquer(l,mid);
	Initialize();
	for(j=l,i=mid+1;i<=r;i++)
	{
		for(;j<=mid&&a[j]->x<=a[i]->x;j++)
			Update(a[j]->y,f[a[j]-mempool]);
		f[a[i]-mempool]=max(f[a[i]-mempool],Get_Ans(a[i]->y));
	}
	CDQ_Divide_And_Conquer(mid+1,r);
	l1=l;l2=mid+1;
	for(i=l;i<=r;i++)
		if( l2>r || l1<=mid && Compare(a[l1],a[l2]) )
			_a[i]=a[l1++];
		else
			_a[i]=a[l2++];
	memcpy(a+l,_a+l,sizeof(a[0])*(r-l+1));
}
namespace Bidimensional_BIT{
	namespace Hash_Table{
		struct List{
			int x,y;
			ld val;
			List *next;
			List() {}
			List(int _,int __,List *___):
				x(_),y(__),val(0),next(___) {}
		}mempool[12000000],*C=mempool,*head[1031][1301];
		int tim[1031][1301],T;
		void Initialize()
		{
			C=mempool;
			++T;
		}
		ld& Hash(int x,int y)
		{
			int pos1=x%1031,pos2=y%1301;
			List *temp;
			if(tim[pos1][pos2]!=T)
				tim[pos1][pos2]=T,head[pos1][pos2]=0;
			for(temp=head[pos1][pos2];temp;temp=temp->next)
				if(temp->x==x&&temp->y==y)
					return temp->val;
			return (head[pos1][pos2]=new List(x,y,head[pos1][pos2]))->val;
		}
		ld Get_Hash(int x,int y)
		{
			int pos1=x%1031,pos2=y%1301;
			List *temp;
			if(tim[pos1][pos2]!=T)
				tim[pos1][pos2]=T,head[pos1][pos2]=0;
			for(temp=head[pos1][pos2];temp;temp=temp->next)
				if(temp->x==x&&temp->y==y)
					return temp->val;
			return 0;
		}
	}
	using namespace Hash_Table;
	void Update(int x,int y,ld val,int flag)
	{
		int i,j;
		for(i=x;i&&i<=n;i+=flag*(i&-i))
			for(j=y;j&&j<=n;j+=flag*(j&-j))
				Hash(i,j)+=val;
	}
	ld Get_Ans(int x,int y,int flag)
	{
		ld re=0;
		int i,j;
		for(i=x;i&&i<=n;i+=flag*(i&-i))
			for(j=y;j&&j<=n;j+=flag*(j&-j))
				re+=Get_Hash(i,j);
		return re;
	}
}
int main()
{
	//freopen("3756.in","r",stdin);
	//freopen("3756.out","w",stdout);
	int i,j;
	cin>>n;
	for(i=1;i<=n;i++)
	{
		scanf("%d%d",&mempool[i].x,&mempool[i].y);
		a[i]=&mempool[i];
	}
	Discretization();
	sort(a+1,a+n+1,Compare);
	CDQ_Divide_And_Conquer(1,n);
	cout<<ans<<endl;
	static int a[M];
	for(i=1;i<=n;i++)
		a[i]=i;
	sort(a+1,a+n+1,_Compare);
	for(i=1;i<=n&&f[a[i]]==1;i++)
		g[a[i]]=1;
	for(j=1;i<=n;i++)
	{
		if(f[a[i]]!=f[a[i-1]])
		{
			for(;f[a[j]]+1!=f[a[i]];j++);
			Bidimensional_BIT::Initialize();
		}
		for(;a[j]<a[i]&&f[a[j]]==f[a[i]]-1;j++)
			Bidimensional_BIT::Update(mempool[a[j]].x,mempool[a[j]].y,g[a[j]],1);
		g[a[i]]=Bidimensional_BIT::Get_Ans(mempool[a[i]].x,mempool[a[i]].y,-1);
	}
	for(i=n;i&&f[a[i]]==ans;i--)
		h[a[i]]=1;
	for(j=n;i;i--)
	{
		if(f[a[i]]!=f[a[i+1]])
		{
			for(;f[a[j]]-1!=f[a[i]];j--);
			Bidimensional_BIT::Initialize();
		}
		for(;a[j]>a[i]&&f[a[j]]==f[a[i]]+1;j--)
			Bidimensional_BIT::Update(mempool[a[j]].x,mempool[a[j]].y,h[a[j]],-1);
		h[a[i]]=Bidimensional_BIT::Get_Ans(mempool[a[i]].x,mempool[a[i]].y,1);
	}
	for(i=1;i<=n&&f[a[i]]==1;i++)
		total+=h[a[i]];
	for(i=1;i<=n;i++)
		printf("%.10lf%c",(double)(g[i]*h[i]/total),i==n?'\n':' ');
	return 0;
}
时间: 2024-10-16 09:12:16

BZOJ 2244 SDOI2011 拦截导弹 CDQ分治/二维树状数组的相关文章

bzoj 2244: [SDOI2011]拦截导弹 cdq分治

2244: [SDOI2011]拦截导弹 Time Limit: 30 Sec  Memory Limit: 512 MBSec  Special JudgeSubmit: 237  Solved: 103[Submit][Status][Discuss] Description 某 国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度.并且能够拦截任意速度的导 弹,但是以后每一发炮弹都不能高于前一发的高度,其拦截的导弹的飞行速度

BZOJ 2244: [SDOI2011]拦截导弹 [CDQ分治 树状数组]

传送门 题意:三维最长不上升子序列以及每个元素出现在最长不上升子序列的概率 $1A$了好开心 首先需要从左右各求一遍,长度就是$F[0][i]+F[1][i]-1$,次数就是$G[0][i]*G[1][i]$ 我们可以用一些转换来简化代码 反转之后变成$LIS$,然后再反转并且$x,y$取反还是$LIS$,写一遍就可以啦 然后本题的树状数组需要维护最大值以及最大值的数量,还有一个时间戳 #include <iostream> #include <cstdio> #include &

BZOJ 2244 [SDOI2011]拦截导弹 ——CDQ分治

三维偏序,直接CDQ硬上. 正反两次CDQ统计结尾的方案数,最后统计即可. #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i) #define D(i,j,k) for (int i=j;i>=i;--i) #define

BZOJ 2738 矩阵乘法(整体二分+二维树状数组)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2738 [题目大意] 给出一个方格图,询问要求求出矩阵内第k小的元素 [题解] 我们对答案的大小进行整体二分,用二维树状数组维护二维区间和, 将超过数量的分治到左区间,不满足的分治到右区间即可. [代码] #include <cstdio> #include <algorithm> #include <cstring> using namespace std;

BZOJ 1452: [JSOI2009]Count (二维树状数组)

Description Input Output Sample Input Sample Output 1 2 HINT 二维树状数组的简单应用,c数组的第一维坐标相当于哈希.如果是修改操作,修改前 将当前的值的个数以及祖先都减1, 修改后将个数加1. #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #include <set> #include

BZOJ 1452 JSOI 2009 Count 二维树状数组

题目大意:有一个m*n的方格,每一个格子有他自己的权值.2种操作: 1.改变一个格子的权值. 2.查询所有的x1 <= x <= x2 && y1 <= y <= y2的中,有多少个格子颜色是c. 思路:好像是二维树状数组的样子,但是不知道怎么搞.后来研究了数据范围,发现格子最大300*300,颜色最多才100种,于是算一下300*300*100*4/1024/1024大概是35M,题目要求64M,可以搞了.(这里算的精确一点,我当时没怎么算,吧颜色开成300的了,

BZOJ 1452: [JSOI2009]Count 二维树状数组

1452: [JSOI2009]Count Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=1452 Description Input Output Sample Input Sample Output 1 2 HINT 题意 题解: 裸的二维树状数组 代码: //qscqesze #include <cstdio> #include <cmath&g

BZOJ 3594 [Scoi2014]方伯伯的玉米田(二维树状数组)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3594 [题目大意] 给出一个数列,选出k个区间使得区间内数全部加1, 求k次操作之后最长的不下降子序列 [题解] 我们发现,每次的区间右端点一定贪心选到最右端才是最优的, 那么在用树状数组统计的时候,只要一个点被+1了,那么后面的点起码+1, 我们在树状数组后面加一维统计被区间包含的次数,发现也是前缀和关系, 所以用二维树状数组统计答案即可. 为避免自身被重复统计,第二维循环降序.

bzoj3262: 陌上花开 二维树状数组

众所周知三维偏序可以树套树或者cdq 然后我就写了二维离散化+二维树状数组 然后被cdq艹飞了(ToT) #include<cstdio> #include<algorithm> #define N 100005 #define J i+(i&-i) #define F lower_bound #define G upper_bound using namespace std; struct io_t{ operator int(){ int x; scanf("