2012百度之星冬季赛第四场第二题 度熊的复仇

题目:

度熊的复仇

 

Time Limit: 1 Seconds   Memory Limit: 65536K

 

度熊是个喜欢打DOTA的人,有一天他打DOTA的时候被对面的拉比克完全压制,于是他决定选一个自己的拉比克去报仇。

现在,度熊的面前有很多敌人,他可以选择对任意一个敌人施放“弱化能流”。“弱化能流”有连锁反应,连锁的时候伤害不会减弱。如果这个人被击中了那么和他距离在D以内的敌人也会同时被击中,同时会一直这样连锁下去直到条件不满足。每次施放“弱化能流”只会对同一个敌人造成1次伤害,当敌人的血量小于等于0时这个敌人就会死去,但是依然存在,就是说还是可以继续被连锁和向外连锁。

度熊的拉比克有个特性就是有“一击必杀”,当他每次施放“弱化能流”时会有p%的概率触发此特性,那么此次的伤害将会直接变成无穷大,同时其余特性不变,即依然有连锁性而且连锁的伤害依然是无穷大。

现在有N个敌人,每个敌人的血是Ki(1 <= i <= n),每次“弱化能流”造成的伤害是Att,每次施放需要消耗m的魔法值。

现在度熊想知道:他把所有敌人全部消灭的魔法消耗期望值是多少?

 

Input

第一行输入一个T代表有T组样例(T <= 20)。

每个样例的第一行输入5个整数n,att,m,p,D,代表有n个敌人,每次“弱化能流”造成的伤害是att,需要消耗m的魔法值,有p%的概率会造成一击必杀,两人的距离在D以内时可以被连锁(1 <= n <= 1000 , 1 <= att <= 100 , 1 <= m <= 10 , 0 <= p <= 100,1 <= D <= 100000)。

接下来n行,每行包含3个整数xi,yi,ki,代表度熊的敌人的位置在(xi,yi),他的血是ki,度熊的敌人可以认为是一个点(1 <= xi,yi <= 150000, 1 <= ki <= 100)。

 

Output

对于每个样例,请输出"Case #c : ans".

"c"代表着是第几个样例,样例从1开始;

"ans"代表消灭所有敌人需要消耗的期望魔法值,要求保留两位小数。

 

 

Sample Input

2

3 1 1 10 5

0 0 5

1 1 7

2 2 10

4 1 4 13 3

0 0 6

1 1 8

9 9 7

10 10 9

 

Sample Output

Case #1: 6.51

Case #2: 42.65

 

特别注意:本题中距离在D以内,意为距离小于D。。。。(导致主号WA的关键句,不过第二天考试RP++了)

 

  首先可以发现,如果很多人连成一片的话,攻击其中任意一个都是等价的。然后就等价于攻击期中K最大的人。

  怎么划分集合,我用的是并查集的方法。

  然后对每个集合,取出其中最大的K,求出没有“一击必杀”需要多少次才能打死。假设计算出来为x次,那么x次打死的概率为(1.0 - p)^(k-1)

  其他的,假设i次打死(i<x),概率为(10. - p)^(i - 1) * p

  乘上次数统计期望。然后各个集合全部相加就好。

代码:

 1 #include<cmath>
 2 #include<cstdio>
 3 using namespace std;
 4 typedef long long ll;
 5 int T,C,n,a,m,f[1001],k[1001];
 6 ll d;
 7 double p,ans;
 8 struct poi{
 9     int x,y;
10     void in(){scanf("%d%d",&x,&y);}
11     ll jl(const poi &b)const{
12         ll i=x-b.x,j=y-b.y;
13         return i*i+j*j;
14     }
15 }s[1001];
16 int gf(int x){return x!=f[x]?f[x]=gf(f[x]):x;}
17 void lk(int i,int j){
18     int x=gf(i),y=gf(j);
19     f[x]=y;
20     if(k[x]>k[y])k[y]=k[x];
21 }
22 double ga(int k){
23     k=k/a+(k%a?1:0);
24     double a1=0.0,ap=1.0;
25     for(int i=1;i<k;i++){
26         a1+=i*ap*p;
27         ap*=1.0-p;
28     }
29     return a1+pow(1.0-p,k-1)*k;
30 }
31 int main()
32 {
33     scanf("%d",&T);
34     while(T--){
35         scanf("%d%d%d%lf%lld",&n,&a,&m,&p,&d);
36         p/=100.0;
37         d*=d;
38         for(int i=0;i<n;i++){
39             s[i].in();
40             f[i]=i;
41             scanf("%d",k+i);
42         }
43         for(int i=0;i<n;i++)
44         for(int j=i+1;j<n;j++)
45             if(s[i].jl(s[j])<=d)lk(i,j);
46         ans=0.0;
47         for(int i=0;i<n;i++)if(f[i]==i)ans+=ga(k[i]);
48         printf("Case #%d: %.2lf\n",++C,ans*m);
49     }
50     return 0;
51 }

 

posted on 2013-01-11 19:10 混沌DM 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/hundundm/archive/2013/01/11/2856921.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值