51nod1834 蛇爬樹(二分查找+細節)

題面在這裏

題意

有一個人在長爲 n 的數軸上從 0 開始,每次向右走一步, xx+1 。同時給出 k 對關鍵點 ai,bi ,如果走到 ai 就要跳到 bi

現在給出他每天走的步數,假如某天他往回走了他的速度會 ×2 。有 q 個詢問,每次給出初始速度問最少多少天走到 n

n109,k,q2×105

做法

//希望自己以後多思考思考。。。不要動不動就放棄

來說這題。

首先可以發現路徑是唯一的,所以可以處理出所有關鍵邊經過的順序,可以用set維護。

然後對於每個詢問就可以二分找到第一條後退邊,模擬即可。由於經過後退邊以後速度會翻倍,就能保證最多隻會操作log次。。

總時間複雜度 O(nlog2n)

代碼

注意細節。//define和qpow啥的不用理會。。。

#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=(x); i<=(y); i++)
#define per(i,x,y) for (int i=(x); i>=(y); i--)
#define ll long long
#define ld long double
#define inf 1000000000
#define INF 1000000000000000000ll
#define pii pair<int,int>
#define F first
#define S second
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define sqr(x) ((x)*(x))
#define cmin(x,y) (x)=(y)<(x)?(y):(x)
#define cmax(x,y) (x)=(y)>(x)?(y):(x)
#define mset(x,y) memset((x),(y),sizeof(x))
#define mcpy(x,y) memcpy((x),(y),sizeof(y))
using namespace std;
const ld pi=acos(-1);
const ld eps=1e-8;
//////////////////////// header files ////////////////////////
ll read(){
    char ch=getchar(); ll x=0; int op=1;
    for (; !isdigit(ch); ch=getchar()) if (ch=='-') op=-1;
    for (; isdigit(ch); ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
    return x*op;
}
void write(ll a){
    if (a<0) putchar('-'),a=-a;
    if (a>=10) write(a/10); putchar(a%10+'0');
}
////////////////////////// fast i/o //////////////////////////
#ifdef mod
ll ksm(ll x,ll p){
    ll ret=1;
    for (; p; p>>=1,x=x*x%mod) if (p&1) ret=ret*x%mod;
    return ret;
}
ll get_inv(ll x){
    return ksm(x,mod-2);
}
#else
ll ksm(ll x,ll p){
    ll ret=1;
    for (; p; p>>=1,x=x*x) if (p&1) ret=ret*x;
    return ret;
}
#endif
//////////////////////////// qpow ////////////////////////////
#define N 200005
int n,m,q,cnt; ll dis[N],b[N],ans;
struct node{
    int x,y,z; node(){}
    node(int x_,int y_,int z_){ x=x_,y=y_,z=z_; }
    bool operator < (const node &t) const{
        return x<t.x;
    }
}a[N];
//#define local
int main(){
#ifdef local
    freopen("test.in","r",stdin); freopen("test.out","w",stdout);
#endif
    n=read(),m=read(); set<node> s;
    rep (i,1,m){
        int x=read(),y=read(),z; if (x>y) z=1; else z=0;
        s.insert(node(x,y,z));
    }
    int now=0;
    while (1){
        set<node>::iterator it=s.lower_bound(node(now,0,0));
        if (it==s.end()) break;
        a[++cnt]=*it; now=(*it).y; s.erase(it);
    }
    /*cerr<<cnt<<endl;
    rep (i,1,cnt){
        cerr<<a[i].x<<' '<<a[i].y<<endl;
    }*/
    //會經過cnt條關鍵邊
    now=0;
    rep (i,1,cnt){
        dis[i]=dis[i-1]+abs(a[i].x-now)+1;//dis[i]表示從0走到第i條邊終點的距離
        b[i]=b[i-1]+a[i].z; now=a[i].y;
    }
    dis[cnt+1]=dis[cnt]+abs(n-now); b[cnt+1]=inf;
    //rep (i,1,cnt+1) cerr<<dis[i]<<' '; cerr<<endl;
    //rep (i,1,cnt+1) cerr<<b[i]<<' '; cerr<<endl;
    //exit(0);
    q=read();
    while (q--){
        ll x=read(),all=0; now=ans=0;
        while (1){
            //找第一條後退邊
            int pos=upper_bound(b+1,b+1+cnt+1,b[now])-b;//b[pos]>b[now]
            //cerr<<"pos: "<<pos<<", now: "<<now<<", all="<<all<<endl;
            if (pos>cnt){
                ans+=(dis[pos]-all-1)/x+1; break;
            }
            if (dis[pos]-all>x){
                int tmp=(dis[pos]-all-1)/x;
                ans+=tmp; all+=(ll)tmp*x;
                continue;
            }
            all+=x;
            pos=upper_bound(dis+1,dis+1+cnt+1,all)-dis-1;
            now=pos; x<<=1; ans++;
            if (all>=dis[cnt+1]) break;
        }
        write(ans),puts("");
    }
    return 0;
}
// sample data:
/*
7 3
2 5
1 3
4 2
2
1
2

6
3

======
12 4
2 7
8 3
5 10
7 6
100
5

*/

// rest:
/*

*/

/*
If you find any bug in my code or solution, please tell me. Thanks so much.
email: [email protected]
*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章