题解 CF97C 【Winning Strategy】

题解 CF97C 【Winning Strategy】

此题是某平台%你赛原题,跟大家分享一下某校zsy和sxr等同学的神仙做法。

我解释一下题意,大是说,我有【无限】个人,每个人可以对他“伤害”至多两次。当伤害完第二次时,这个人会转变成一个贡献(Pi)。Pi和杀死的人数正相关。

Idea:

其实此题关键是“无限”这个关键词,这说明,操作到了极远的时候,其实是几种操作在互刷,稍后我们将证明,有且仅有特定两种操作在互刷。

为了方便讨论,我们定义:

  • 没有被操作过的人称为“正常人”
  • “操作”一次的人称为“伤员”
  • 伤员被再次操作称为杀死或者牺牲
  • 某次(种)操作产生的伤员个数称作他的贡献

开始解题

  1. 考虑每种操作的贡献,显然,每次操作除了可以杀死伤员之外,应尽量产生多的伤员。这样显然更优。
  2. 此外,由于是在极远的操作时求平均值,故我们可以假设某种互刷模式已经开始死循环,不需要铺垫条件,或者铺垫条件对答案的影响被忽略
  3. 互刷的条件显然是,某些操作构成的集合造成的贡献(伤员个数),是另一个集合伤员的来源,这样显然最优。
  4. 对于由三种操作构成的互刷模式和由两种操作构成的模式,我们不妨令那三种互刷模式:P1<P2<P3,则它们需要的伤员数为n1<n2<n3
    • 当P1和P2专注刷伤员,而P3专注杀死伤员时,P1和P2产生的对答案的影响是(P1+P2)/2,由于单调递增,则(P1+P2)/2 < P2 ,则这种操作不如干脆P2自己单独刷。因为当P1刷了n1*n2次时,不如P1自己刷。而在无限远的地方,就会产生这样的转变。
    • 当P1专注刷伤员,而P2和P3专注杀死伤员时同理可证。

(其实是五)

可以知道,下标<n/2的,是产生伤员的,下标>(n/2)+1的,是消耗伤员的,若n为偶数,则下标n/2那个可以自己和自己刷。他们互刷的比例与他们的最小公倍数有关。

至此,我们引进第一种神仙做法,时间复杂度O(n2)

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
double s[105],ans=0;
int w[105];
int gcd(int x,int y){
	if(x%y==0)return y;
	else return gcd(y,x%y);
}
int main(){
	int n;
	cin>>n;
	for(int i=0;i<=n;i++)
	{
		cin>>s[i];
		w[i]=n-i*2;//正贡献为正,负贡献为负
	}
	cout<<endl;
	for(int i=0;i<=(n-1)/2;i++)
		for(int l=(n/2)+1;l<=n;l++){
			int t=gcd(abs(w[i]),abs(w[l]));
			int m=abs(w[i])*abs(w[l])/t;
			//最小共倍数 
			int x1=m/abs(w[i]);
			int x2=m/abs(w[l]);
			ans=max(ans,(s[i]*x1+s[l]*x2)/(x1+x2));
		}
	ans=max(ans,s[n/2]);//我刷自己
	printf("%.10lf",ans);
	return 0;
}

但是,我们可以大胆猜测!是不是把mid之前的(P/贡献)最大和mid之后的(P/贡献)最大加起来除以二就好了呢?

于是O(n)算法就出来了。读入时扫描,前面一半和后面一个分别找到最大的,然后就玄学了。

posted @ 2019-01-23 11:39  谁是鸽王  阅读(273)  评论(0编辑  收藏  举报