【34.68%】【BZOJ 3616】War

Time Limit: 20 Sec   Memory Limit: 256 MBSec   Special Judge
Submit: 173   Solved: 60
[ Submit][ Status][ Discuss]

Description

小x所在的世界正在经历一场在k个阵营之间的战争。每个阵营有若干个炮塔,每个炮塔由攻击系统和防御系统组成。第i个炮塔可以攻击到离它欧几里德距离小于等于ri 或者曼哈顿距离小于等于ai的炮塔,被攻击到的炮塔防御系统就会崩溃,同一联盟的炮塔不会被攻击到。每次会随机选择一个炮塔攻击它能打到的所有炮塔,问进行m轮后期望剩下多少个阵营,使得这些阵营拥有的炮塔的防御系统全部完好。防御系统崩溃的炮塔还是会被打到的。值得注意的是,如果一个联盟没有任何炮塔,那么不管怎样它都是完好的。

Input

  输入文件第一行有三个整数n、m、k,分别表示炮塔数目、攻击轮数以及联盟个数。 接下去n行每行有五个正整数xi、yi、ri、ai、pi,其中xi、yi表示炮塔坐标,p表示炮塔所属于阵营。

Output

  输出一行一个实数表示答案,你的输出与标准输出的误差在1e-3以内会被认为是正确的。

Sample Input

2 2 3
0 0 2 2 1
1 1 2 2 2

Sample Output

1.500

HINT

  100%的数据, 0 <= n、m、k <= 35000,1 <= pi <= k,其余所有数字均非负且小于10000,保证在数据生成时不考虑一个点的具体位置,而将它的横纵坐标分开考虑。


Source

【题解】

计算期望的方法是。每个联盟中所有的塔会被多少个其他联盟的塔攻击到,假如为xi(是塔的总数,而不是说这些塔所属的不同联盟的总数);

则期望为∑(1-xi/n)^m  (i∈[1..k])

就是说每一轮中选择剩下的n-x个塔,这个联盟的所有塔就都不会被炸到了。

好好理解理解吧。

然后是实现问题。

以塔的坐标为节点建立kd-tree。用维护的ma_x[2],mi_n[2](最大x,y,最小x,y来确定树中记录的塔是否能被当前枚举的第i号塔炸掉);

如果某个节点的子树的所有坐标中离当前枚举的塔的最大距离都在当前枚举的塔的攻击范围内。那么就不用继续往下搜索了。直接用一个类似线段树的懒惰标记的东西记录该节点包括该节点以下的节点都会被当前枚举的塔炸到(记录的话用bitset);

具体的实现看代码

获取当前枚举的塔和树中的塔的距离的最小值(最大值不用这个)用了估价函数的原理。

就是如果在子树的所有节点所能形成的最大矩形内部。则返回0。

否则返回这个点到这个矩形(不一定存在只是假想的)的边缘的距离。

【代码】

/**************************************************************
Problem: 3616
User: chengchunyang
Language: C++
Result: Accepted
Time:14376 ms
Memory:216980 kb
****************************************************************/

#include <cstdio>
#include <vector>
#include <bitset>
#include <algorithm>
#include <cmath>

using namespace std;

const int MAXN = 40000;

struct point
{
	int d[2], mi_n[2], ma_x[2], bianhao, l, r;
};
//dot[i]记录编号为i的塔在树中的节点编号。
int n, m, k, root, ou[MAXN], man[MAXN], dot[MAXN], number[MAXN], now;
vector <int> a[MAXN];
vector <int> dandu[MAXN];
point t[MAXN];
bitset <MAXN> be_attacked[MAXN];
double ans;

void input_data()
{
	scanf("%d%d%d", &n, &m, &k);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d%d%d%d%d", &t[i].d[0], &t[i].d[1], &ou[i], &man[i], &number[i]);
		a[number[i]].push_back(i);
		t[i].bianhao = i;
	}
}

bool cmp(point a, point b)
{
	return a.d[now] < b.d[now];
}

void up_data(int rt)
{
	int l = t[rt].l, r = t[rt].r;
	for (int i = 0; i <= 1; i++)
	{
		if (l)
		{
			t[rt].ma_x[i] = max(t[rt].ma_x[i], t[l].ma_x[i]);
			t[rt].mi_n[i] = min(t[rt].mi_n[i], t[l].mi_n[i]);
		}
		if (r)
		{
			t[rt].ma_x[i] = max(t[rt].ma_x[i], t[r].ma_x[i]);
			t[rt].mi_n[i] = min(t[rt].mi_n[i], t[r].mi_n[i]);
		}
	}
}

int build(int begin, int end, int fx)
{
	int m = (begin + end) >> 1;
	now = fx;
	nth_element(t + begin, t + m, t + end + 1, cmp);
	dot[t[m].bianhao] = m;
	for (int i = 0; i <= 1; i++)
		t[m].ma_x[i] = t[m].mi_n[i] = t[m].d[i];
	if (begin < m)
		t[m].l = build(begin, m - 1, 1 - fx);
	if (m < end)
		t[m].r = build(m + 1, end, 1 - fx);
	up_data(m);
	return m;
}

int get_manhadun(int rt, int x, int y, int fanwei)
{
	int d[2];
	d[0] = x; d[1] = y;
	int temp1 = 0;
	for (int i = 0; i <= 1; i++)
	{
		temp1 += max(d[i] - t[rt].ma_x[i], 0);
		temp1 += max(t[rt].mi_n[i] - d[i], 0);
	}
	int temp2 = 0;
	for (int i = 0; i <= 1; i++)
		temp2 += max(abs(d[i] - t[rt].ma_x[i]), abs(d[i] - t[rt].mi_n[i]));
	if (temp2 <= fanwei)//如果最远都在里面了则肯定整个子树都在攻击范围内
		return 1;
	if (temp1 <= fanwei)//如果最近在。则只能说明有一部分是在攻击范围内的,不能一概而论
		return 2;
	return 0;//两个都不满足就全都在攻击范围外。就没有必要往下找了
}

int sqr(int x)
{
	return x*x;
}

int get_ou(int rt, int x, int y, int fanwei)//欧几里得距离和曼哈顿距离的类似
{
	int temp1 = 0, d[2];
	d[0] = x; d[1] = y;
	for (int i = 0; i <= 1; i++)
	{
		int a, b;
		a = d[i] - t[rt].ma_x[i];
		b = t[rt].mi_n[i] - d[i];
		if (a > 0)
			temp1 += sqr(a);
		if (b > 0)
			temp1 += sqr(b);
	}
	int temp2 = 0;
	for (int i = 0; i <= 1; i++)
		temp2 += max(sqr(t[rt].ma_x[i] - d[i]), sqr(t[rt].mi_n[i] - d[i]));
	if (temp2 <= sqr(fanwei))
		return 1;
	if (temp1 <= sqr(fanwei))
		return 2;
	return 0;
}

void sear_ch(int rt, int num, int x, int y)
{//num是枚举的塔的编号,rt是这个节点的编号。x,y是第num个塔的横纵坐标
	int gujia_man;
	gujia_man = get_manhadun(rt, x, y, man[num]);
	if (gujia_man == 1)
	{
		be_attacked[rt][num] = 1;//这个节点所代表的塔以及它所在的子树能被id塔攻击到
		return;
	}
	int gujia_ou;
	gujia_ou = get_ou(rt, x, y, ou[num]);
	if (gujia_ou == 1)
	{
		be_attacked[rt][num] = 1;//同理
		return;
	}
	if (!gujia_man && !gujia_ou)//两种距离都没办法攻击到。则没有必要往下找
		return;
	if ((sqr(t[rt].d[0] - x) + sqr(t[rt].d[1] - y)) <= sqr(ou[num]) ||
		abs(t[rt].d[0] - x) + abs(t[rt].d[1] - y) <= man[num])
		dandu[rt].push_back(num); //这是单个的情况就是说没办法确定子树是不是能被炸到
	if (t[rt].l)
		sear_ch(t[rt].l, num, x, y);
	if (t[rt].r)
		sear_ch(t[rt].r, num, x, y);
}

void down_push(int rt)
{
	int l = t[rt].l, r = t[rt].r;
	if (l)
	{
		be_attacked[l] |= be_attacked[rt];//往下传递这个节点能被那些塔攻击到。
		down_push(l);
	}
	if (r)
	{
		be_attacked[r] |= be_attacked[rt];
		down_push(r);
	}
	int len = dandu[rt].size();
	for (int i = 0; i <= len - 1; i++)//这些是单独只有o能被攻击到的,不一定它的子树也能被攻击到。
		be_attacked[rt][dandu[rt][i]] = 1;
}

void get_ans()
{
	root = build(1, n, 0);
	for (int i = 1; i <= n; i++)
	{
		int x = t[dot[i]].d[0], y = t[dot[i]].d[1];
		sear_ch(root, i, x, y);
	}
	down_push(root);
	ans = 0;
	for (int i = 1; i <= k; i++)
	{
		bitset <MAXN> temp;
		temp.reset();
		int len = a[i].size();
		for (int j = 0; j <= len - 1; j++)
		{
			int x = a[i][j];
			x = dot[x];
			temp |= be_attacked[x];
		}
		for (int j = 0; j <= len - 1; j++)
			temp[a[i][j]] = 0;
		int num = temp.count();
		ans += pow(1 - (num*1.0) / n, m*1.0);
	}
}

void output_ans()
{
	printf("%.3lf\n", ans);
}

int main()
{
	//freopen("F:\\rush.txt", "r", stdin);
	input_data();
	get_ans();
	output_ans();
	return 0;
}




转载于:https://www.cnblogs.com/AWCXV/p/7632242.html

weixin151云匹面粉直供微信小程序+springboot后端毕业源码案例设计 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值