Gym103049F

Gym103049F

注意到任意碰撞只会发生在还存在的两个相邻的石块中。

我们对记录下每一个可能发生碰撞的时间。存入一个优先队列里,队首是最先发生的碰撞。

再使用一个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('
');
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            o(i);putchar(' ');
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/yesuweiYYYY/p/14671990.html