注意到任意碰撞只会发生在还存在的两个相邻的石块中。
我们对记录下每一个可能发生碰撞的时间。存入一个优先队列里,队首是最先发生的碰撞。
再使用一个set维护还存在的石头。同时用vis记录一下。
每次弹出队首,一个碰撞,若此碰撞的两石子都存在,那么本碰撞发生,将两个石头从set中删除。
再将删去后可能发生的新碰撞,即本次碰撞两个石子的前面的和后面的另两个石子(可能不存在)加入队列中。
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
void in(int &x){
x=0;char c=getchar();
int y=1;
while(c<‘0‘||c>‘9‘){if(c==‘-‘)y=-1;c=getchar();}
while(c>=‘0‘&&c<=‘9‘){x=(x<<3)+(x<<1)+c-‘0‘;c=getchar();}
x*=y;
}
void o(int x){
if(x<0){x=-x;putchar(‘-‘);}
if(x>9)o(x/10);
putchar(x%10+‘0‘);
}
int n;
long double a[N],v[N];
struct collision{
int l,r;
long double w;
bool operator <(const collision &t)const{
return w > t.w;
}
};
priority_queue<collision>q;
set<int>st;
bool vis[N];
void PUSH(int x1,int x2){
if(v[x1]<=v[x2])return;//碰不到。
q.push((collision){x1,x2,(a[x2]-a[x1])/(v[x1]-v[x2])});
}
void Drop(int x,int x2){
if(vis[x]||vis[x2])return;//有石子已经不存在了
vis[x]=true;vis[x2]=true;//vis记录石头被丢了
//查找可能发生的新碰撞
auto iter = st.lower_bound(x);
auto iter2 = st.lower_bound(x2);
int Next = -1,Last = -1;
if(next(iter2)!=st.end()){
Next = *next(iter2);
}
if(iter!=st.begin()){
Last = *prev(iter);
}
if(Last!=-1&&Next!=-1)PUSH(Last,Next);//若新碰撞存在加入优先队列里
st.erase(x);st.erase(x2);
}
signed main(){
in(n);
for(int i=1;i<=n;i++){
scanf("%llf%llf",&a[i],&v[i]);
}//读入
for(int i=1;i<=n;i++)st.insert(i);//初始化st为1-n
for(int i=1;i<n;i++){
PUSH(i,i+1);//将碰撞加入队列
}
while(!q.empty()){
collision t = q.top();q.pop();//队首
Drop(t.l,t.r);//重新处理
}
int ans=0;
for(int i=1;i<=n;i++){
if(!vis[i])ans++;
}
o(ans);putchar(‘\n‘);
for(int i=1;i<=n;i++){
if(!vis[i]){
o(i);putchar(‘ ‘);
}
}
return 0;
}
原文:https://www.cnblogs.com/yesuweiYYYY/p/14671990.html