Codeforces Round #456 (Div. 2) 题解

Codeforces Round #456 (Div. 2)

Codeforces 912C

题意

本题题意较为复杂,是以MOBA游戏作为背景的:直接放链接吧

解题思路

其实本题的Note就是对解决本题的绝佳提示,我们可以用扫描线来处理该区域能击杀多少个英雄,该区域的最佳使用大招的时间就是该区域结束的时间。

需要注意的是判断最优价值无限大的条件:

1.价值增长速率不为\(0\)

2.有英雄最大生命值小于等于大招的伤害 或 有英雄生命恢复速率为0,并且初始生命值小于大招的伤害

实现上,用set或者优先队列管理时间线,用两个map分别管理某个时间点能击杀的英雄增加了多少,和减少了多少

AC代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
typedef pair<int,int> pii;
set <long long> timeline;
map <long long,int> erased,added;
vector <pii> enemy[maxn];
int regen[maxn],maxhealth[maxn],n,m,bounty,increase,damage; 
int main(int argc, char const *argv[])
{
	scanf("%d%d",&n,&m);
	scanf("%d%d%d",&bounty,&increase,&damage);
	for (int i=1;i<=n;i++)
	{
		int sh;
		scanf("%d%d%d",&maxhealth[i],&sh,&regen[i]);
		enemy[i].push_back(make_pair(0,sh));
	}
	for (int i=1;i<=m;i++)
	{
		int t,e,h;
		scanf("%d%d%d",&t,&e,&h);
		enemy[e].push_back(make_pair(t,h));
	}

	for (int i=1;i<=n;i++)
	{
		vector<pii> &v=enemy[i];
		sort(v.begin(),v.end());

		if(increase&&(damage>=maxhealth[i]||(!regen[i]&&v[0].first<=damage))){
			//cout<<(int)(damage>=maxhealth[i])<<(int)(!regen[i]&&v[0].first<=damage)<<endl;
			puts("-1");
			return 0;
		}

		for (int j=0;j<v.size();j++)
		{
			if(v[j].second>damage)	continue;

			long long nxtpos=2e9;
			if(regen[i])	nxtpos=min(nxtpos,1ll*(damage-v[j].second)/regen[i]);
			if(j!=v.size()-1)	nxtpos=min(nxtpos,1ll*(v[j+1].first-v[j].first-1));

			added[v[j].first]++;
			erased[v[j].first+nxtpos]++;


			timeline.insert(v[j].first);
			timeline.insert(v[j].first+nxtpos);

		}

	}

	long long ans=0,cnt=0;
	for (auto i: timeline){
		cnt+=added[i];
		ans=max(ans,cnt*(bounty+1ll*increase*i));
		cnt-=erased[i];
	}

	cout<<ans<<endl;	
	return 0;
} 

Codeforces 912D

题意

给定\(n \times m\) 的鱼塘和\(r \times r\) 的渔网,投放\(k\)条鱼,求能捕到鱼数最大的期望

解题思路

首先不难想到转化为投放在某个位置的期望:$ \frac{\text{这个位置能被多少渔网覆盖}}{r\times r}$

进而贪心地选择期望最大的\(k\)个位置就行了,但如果要求出所有\(n \times m\)个值进而求出前\(k\)个值是过不了的,不过可以发现更“靠近”中心的位置更优(显然的),所以我们从中心位置出发做\(BFS\),在已扩展的点中选择最优值就可以了。

这题的教训:map的去重不是看数据内容是否相同的,也不是要重载== 运算符,而是一般要对象及对象中的属性对象的<操作符进行重载,重载的<操作应满足“严格弱排序”。map并不是用==操作符来判断两个对象是否相等的,而是两个对象相互都不“小于”时认为两个对象相等。

复杂度\(O(k \log k)\)

AC代码

#include <bits/stdc++.h>
using namespace std;
int n,m,r,k;
long long solve(int i,int j)
{
	int tx=min(n,i+r-1)-max(i-r+1,1)-r+2;
	int ty=min(m,j+r-1)-max(j-r+1,1)-r+2;
	return 1ll*tx*ty;
}
struct node
{
	int x,y,z;
	node(int _x=1,int _y=1):x(_x),y(_y){z=solve(x,y);};

	bool operator < (const node& rhs) const
	{
		return z<rhs.z;
	}
};
int dx[]={-1,1,0,0},dy[]={0,0,-1,1};
map < pair<int,int> , bool > vis;
priority_queue<node> Q;
long long bfs()
{
	Q.push(node((n+1)/2,(m+1)/2));
	vis[make_pair((n+1)/2,(m+1)/2)]=true;
	long long res=0;
	while (k--)
	{
		node u=Q.top();
		Q.pop();
		res+=u.z;
		for (int i=0;i<4;i++){
			node v=node(u.x+dx[i],u.y+dy[i]);
			if(vis[make_pair(v.x,v.y)])	continue;
			if(v.x<1||v.x>n||v.y<1||v.y>m)	continue;
			vis[make_pair(v.x,v.y)]=true;
			Q.push(v);
		}
	}
	return res;
}
int main(int argc, char const *argv[])
{
	cin>>n>>m>>r>>k;
	printf("%.10f\n",1.0*bfs()/(1.0*(n-r+1)*(m-r+1)));
}

Codeforces 912E

题意

给定素数集合\(p\)\(Card\ p \le 16\)\(p_i \le 100\),设集合\(S=\text{素因子只在}p\text{中的集合}\), 求\(S\)中第\(k\)大的数,保证这个数\(\le 1e18\)

解题思路

如果直接求\(S\),复杂度难以接受,这里我们将集合\(p\)拆成\(A\)\(B\),那么\(S\)中的每个元素都是\(A\)\(B\)中元素的积,可以通过双指针法来求\(S\)中小于某个数有多少个值,进而我们可以通过二分法求出答案。

时间复杂度\(O(\log\ 1e18\ (S_A+S_B) )\)

对于\(A,B\)的选择,不是简单地分成两份,因为素因子小的集合产生的数多,经测试,排序后将前6个数分出来最坏情况下两个集合大小都不超过\(1e6\),其他的数都不够优

AC代码

#include <bits/stdc++.h>
using namespace std;
int prime[20],n,k;
const long long maxnum=1e18;
vector <long long> A,B;
void dfs(long long value,int from,int to,vector<long long> &out)
{
	out.push_back(value);
	for (int i=from;i<=to;i++)
		if(maxnum/prime[i]>=value)
			dfs(value*prime[i],i,to,out);
}
long long solve(long long x)
{
	int j=0;
	long long res=0;
	for (int i=A.size()-1;i>=0;i--)
	{
		while(j<B.size()&&B[j]<=x/A[i])
			j++;
		res+=j;
	}
	return res;
}
int main(int argc, char const *argv[])
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%d",&prime[i]);
	sort(prime+1,prime+n+1);
	scanf("%d",&k);
	dfs(1,1,min(6,n/2),A);
	dfs(1,min(n/2,6)+1,n,B);
	sort(A.begin(),A.end());
	sort(B.begin(),B.end());
	//cout<<A.size()<<" "<<B.size()<<endl;
	long long l=1,r=maxnum,mid,ans;
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(solve(mid)>=k)
		{
			ans=mid;
			r=mid-1;
		}
		else
			l=mid+1;
	}
	cout<<ans<<endl;
	return 0;
}
posted @ 2018-02-22 12:09  阿瓦隆的精灵  阅读(282)  评论(0编辑  收藏  举报