Codeforces 625E Frog Fights (Codeforces Round #342 (Div. 2) E) 题解

题目来源:http://codeforces.com/problemset/problem/625/E


题意:有n只青蛙和一个有m个格子的环形桌面,格子从1到m编号,青蛙也从1到n编号,每只青蛙都有一个初始的格子和初始移动距离,从第一只青蛙开始移动,在移动路径上的青蛙将会被撞出游戏,每撞一只青蛙,移动距离减小1,然后下一只未被踢出游戏的青蛙开始移动,从1到n都移动或被踢出游戏后再循环一次,直到将不再有青蛙被踢出游戏时游戏结束,输出还在场上的青蛙的个数,并输出这些青蛙。


解题思路:我们可以先计算出桌面上相邻两只青蛙碰撞时前一只青蛙要走的步数,最先发生碰撞的一定是步数最少且编号最小的青蛙,踢出被撞的青蛙后,由于青蛙的移动距离减小,改变与移动的青蛙相邻的青蛙之间步数,再进行一次上述操作,知道不会有青蛙被撞为止。其实在这个过程中,青蛙相撞要走的步数都事先被计算好了,之后并不要模拟青蛙的移动。可以用pair<int,int>储存青蛙与下一只青蛙相撞要走的步数和青蛙的编号,把pair放入set中就会自动拍好序啦!


代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<set>
using namespace std;
int n,m;
int p[100000+10],a[100000+10];//p[]表示编号为i的青蛙的坐标,a[]表示编号为i的青蛙跳的距离
int x[100000+10];//x[]表示排第i位的青蛙的编号
int nextt[100000+10],pre[100000+10];
bool cmp(int i,int j)
{
	return p[i]<p[j];
}
typedef pair<int,int>pii;
set<pii>st;
set<pii>::iterator it;
const int inf=0x3f3f3f3f;
int dis(int i,int j)//返回i青蛙到j青蛙要跳多少步,如果返回inf则不会撞到
{
	if(i==j)return inf;
	int d=(p[j]-p[i]+m)%m;//d表示编号为i的青蛙到j的距离
	if(i>j) d=(d+a[j])%m;
	if(d<=a[i])return 1;
	if(a[i]<=a[j])return inf;
	int s=a[i]-a[j];
	return (d-a[j]-1)/s+1;//设要走x步相撞,由方程x*a[i]-(x-1)*a[j]=d得,x=(d-a[j])/s,但由于x不一定是整数,所以x=(d-a[j]-1)/s+1
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){scanf("%d%d",&p[i],&a[i]);p[i]--;x[i]=i;}
	sort(x+1,x+n+1,cmp);//使青蛙按坐标从小到大排列
	for(int i=1;i<=n;i++)//创建双链表模拟环形桌面上的青蛙
	{
		if(i<n) nextt[x[i]]=x[i+1];
		else nextt[x[i]]=x[1];
		pre[nextt[x[i]]] =x[i];
	}
	for(int i=1;i<=n;i++)
	{
		st.insert(make_pair(dis(i,nextt[i]),i));
	}
	while(!st.empty())
	{
		it=st.begin();
		if(it->first==inf)break;
		int i=it->second;
		st.erase(it);
		st.erase(make_pair(dis(nextt[i],nextt[nextt[i]]),nextt[i]));
		st.erase(make_pair(dis(pre[i],i),pre[i]));
		p[i]+=dis(i,nextt[i]);//发生碰撞后青蛙的移动距离减小1,但前面的移动已经发生,会对相邻的两只青蛙的碰撞步数产生影响,所以要加上步数*1抵消影响
		a[i]--;
		nextt[i]=nextt[nextt[i]];//删除被撞的青蛙
		pre[nextt[i]]=i;
		st.insert(make_pair(dis(pre[i],i),pre[i]));//重新计算相邻青蛙的步数
		st.insert(make_pair(dis(i,nextt[i]),i));
	}
	printf("%d\n",st.size());
	for(it=st.begin();it!=st.end();it++)
	{
		if(it!=st.begin())printf(" ");
		printf("%d",it ->second);
	}
	printf("\n");
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值