2017.10.13 T2 1978
样例数据
输入
2
2 4
0 2
3 2
10 100
56 89 62 71 7 24 83 1 47 52
9 -16 34 -38 47 49 -32 17 39 -9
输出
2/1
37/7
分析:考试的时候我写了60%的O(
n2logn
)暴力
30%:真正的暴力,模拟每个人走的每一步。
60%:我是把两两相遇的时间算出来,并sort一遍,按照时间顺序模拟相遇,被淘汰的打标记,之后又枚举到他的时候就跳过,最后一次相遇便是答案。更优的方法是,从n开始向前,枚举每个比他编号小的点与他相遇的时间并更新他们被淘汰的时间(取min,复杂度O(
n2
)),最后for循环编号1——n-1的选手,最后被淘汰的便是答案。
100%:只关心每个人相邻的选手,计算他们相遇的时间,每次找最小时间那一个(用优先队列实现),淘汰较小编号的,把这个人左右的人更新为相邻的人,最后淘汰的那一次的时间便是答案,复杂度O(
nlogn
)。
代码
60%:完全就是道判断题(比正解还长orz)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;
int getint()
{
int sum=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-')
{
f=-1;
ch=getchar();
}
for(;isdigit(ch);ch=getchar())
sum=(sum<<3)+(sum<<1)+ch-48;
return sum*f;
}
struct node1{
int d,v;
bool bj,dead;
}man[1010];
struct node2{
int fro,to;
int fz,fm;
}tim[1000010],ans;
int T,n,l,cnt;
int gcd(int x,int y)
{
int m;
if(x>y)
swap(x,y);
while(x%y!=0)
{
m=x%y;
x=y;
y=m;
}
return y;
}
bool comp(const node2 &a,const node2 &b)
{
if(a.fm==b.fm) return a.fz<b.fz;
return (long long)a.fz*b.fm<(long long)a.fm*b.fz;
}
int main()
{
freopen("run.in","r",stdin);
freopen("run.out","w",stdout);
T=getint();
while(T--)
{
n=getint(),l=getint();
cnt=0;//清零
for(int i=1;i<=n;++i)
{
man[i].bj=0;
man[i].dead=0;
}
for(int i=1;i<=n;++i)
man[i].d=getint();
for(int i=1;i<=n;++i)
{
man[i].v=getint();
if(man[i].v>0) man[i].bj=1;
else man[i].v=-man[i].v;
}
for(int i=1;i<n;++i)
for(int j=i+1;j<=n;++j)
{
if(man[i].bj==man[j].bj)
{
if(man[i].bj==1)
{
if(man[i].v>man[j].v)
{
int dis=man[j].d-man[i].d;
if(dis<0)
dis+=l;
tim[++cnt].fro=i;
tim[cnt].to=j;
int g=gcd(dis,man[i].v-man[j].v);
tim[cnt].fz=dis/g;
tim[cnt].fm=(man[i].v-man[j].v)/g;
}
else
{
int dis=man[i].d-man[j].d;
if(dis<0)
dis+=l;
tim[++cnt].fro=i;
tim[cnt].to=j;
int g=gcd(dis,man[j].v-man[i].v);
tim[cnt].fz=dis/g;
tim[cnt].fm=(man[j].v-man[i].v)/g;
}
}
else
{
if(man[i].v>man[j].v)
{
int dis=man[i].d-man[j].d;
if(dis<0)
dis+=l;
tim[++cnt].fro=i;
tim[cnt].to=j;
int g=gcd(dis,man[i].v-man[j].v);
tim[cnt].fz=dis/g;
tim[cnt].fm=(man[i].v-man[j].v)/g;
}
else
{
int dis=man[j].d-man[i].d;
if(dis<0)
dis+=l;
tim[++cnt].fro=i;
tim[cnt].to=j;
int g=gcd(dis,man[j].v-man[i].v);
tim[cnt].fz=dis/g;
tim[cnt].fm=(man[j].v-man[i].v)/g;
}
}
}
else
{
if(man[i].bj==1)
{
int dis=man[j].d-man[i].d;
if(dis<0)
dis+=l;
tim[++cnt].fro=i;
tim[cnt].to=j;
int g=gcd(dis,man[i].v+man[j].v);
tim[cnt].fz=dis/g;
tim[cnt].fm=(man[i].v+man[j].v)/g;
}
else
{
int dis=man[i].d-man[j].d;
if(dis<0)
dis+=l;
tim[++cnt].fro=i;
tim[cnt].to=j;
int g=gcd(dis,man[i].v+man[j].v);
tim[cnt].fz=dis/g;
tim[cnt].fm=(man[i].v+man[j].v)/g;
}
}
}
sort(tim+1,tim+cnt+1,comp);
for(int i=1;i<=cnt;++i)
if(!man[tim[i].fro].dead&&!man[tim[i].to].dead)
{
ans.fz=tim[i].fz;ans.fm=tim[i].fm;
if(tim[i].fro>tim[i].to)
man[tim[i].to].dead=1;
else
man[tim[i].fro].dead=1;
}
cout<<ans.fz<<"/"<<ans.fm<<'\n';
}
return 0;
}
100%:实现略显复杂……
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;
int getint()
{
int sum=0,f=1;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-')
{
f=-1;
ch=getchar();
}
for(;isdigit(ch);ch=getchar())
sum=(sum<<3)+(sum<<1)+ch-48;
return sum*f;
}
const int N=1e5+5,INF=1e9+5;
int T,n,L;
struct node{
int s,v,l,r,id;
inline friend bool operator < (const node &a,const node &b)//自定义结构体的小于符号,就不用在结构体比较的时候写if(a.s<b.s)了,直接写if(a<b)
{
return a.s<b.s;
}
}a[N];
struct meet{
int x,y;
double t;
inline friend bool operator < (const meet &a,const meet &b)//由于优先队列是从大到小排序,那么我们自定义<的意思是大于,就可以从小到大排序啦
{
return a.t>b.t;
}
};
priority_queue<meet> que;
bool del[N];
int gcd(int a,int b)//欧几里得求最大公因数(感觉是为了考一考这个知识点才强行要求输出最简分数的orz)
{
int m;
while(a%b!=0)
{
m=b;
b=a%b;
a=m;
}
return b;
}
double gettime(int x,int y)//计算相遇时间(相向、同向都可以,因为速度有负数)
{
if(a[x].s>a[y].s) swap(x,y);
if(a[x].v>a[y].v)
return (a[y].s-a[x].s)*1.0/(a[x].v-a[y].v);
return (L-(a[y].s-a[x].s))*1.0/(a[y].v-a[x].v);
}
void writeans(int x,int y)//求最简分数输出答案
{
int dis,dv,r;
if(a[x].s>a[y].s)swap(x,y);
if(a[x].v>a[y].v)
{
dis=a[y].s-a[x].s,dv=a[x].v-a[y].v;
r=gcd(dis,dv);
dis/=r,dv/=r;
}
else
{
dis=L-(a[y].s-a[x].s),dv=a[y].v-a[x].v;
r=gcd(dis,dv);
dis/=r,dv/=r;
}
cout<<dis<<"/"<<dv<<'\n';
}
int main()
{
freopen("run.in","r",stdin);
freopen("run.out","w",stdout);
T=getint();
while(T--)
{
n=getint(),L=getint();
memset(del,0,sizeof(del));//清零操作
while(!que.empty())
que.pop();
for(int i=1;i<=n;++i)
a[i].s=getint(),a[i].id=i;//记录每个人的编号
for(int i=1;i<=n;++i)
a[i].v=getint();
sort(a+1,a+n+1);
for(int i=1;i<=n;++i)
a[i].l=i-1,a[i].r=i+1;//记录左右的人(链表)
a[1].l=n,a[n].r=1;
for(int i=1;i<=n;++i)
que.push((meet){i,a[i].r,gettime(i,a[i].r)});//加入优先队列
for(int i=1;i<=n-2;++i)//淘汰n-2个人,最后一个被淘汰的留着用来算ans
{
while(del[que.top().x]||del[que.top().y])
que.pop();//相遇时间最短的两个人如果其中一个已经被淘汰了,那么这个时间就作废了,从队列中弹出
int x=que.top().x,y=que.top().y,z;//找到一对能相遇的人
que.pop();
if(a[x].id<a[y].id)
swap(x,y);
del[y]=true;//编号较小的被淘汰
if(y==a[x].l)
z=a[y].l,a[x].l=z,a[z].r=x;//把这个人左右两边的人接在一起
else
z=a[y].r,a[x].r=z,a[z].l=x;
que.push((meet){x,z,gettime(x,z)});//新的这对人相遇时间入队
}
while(del[que.top().x]||del[que.top().y])
que.pop();//同上面的理弹出
writeans(que.top().x,que.top().y);//最后两个人相遇的时间便是答案
}
return 0;
}
本题结。