1. 程式人生 > >jzoj 5865. 【NOIP2018模擬9.11】假期旅行 線段樹

jzoj 5865. 【NOIP2018模擬9.11】假期旅行 線段樹

Description 這裡寫圖片描述

Input 這裡寫圖片描述

Output 這裡寫圖片描述

Sample Input

5 4 3 1 4 1 2 5 3 2 3 2 4 5 2 3 1 5 3 5 4 5

Sample Output

-1 2 1

Data Constraint 這裡寫圖片描述

Hint 這裡寫圖片描述

分析: 我們設a[i]為從城市i開始,最遠能到達的城市。這樣就連成了一個森林。也就是說,對於一個詢問l,r,就是在森林上面從l開始,最少跳多少步,可以到達一個大於等於r的節點。這個可以倍增求。 關鍵是求a[i]了,考慮每一個座位,如果在[l,r]都是空的,那麼

[l,r]的這些點的a[i]至少是r,也就是一個區間修改和查詢最大值,線段樹維護即可。 我們可以把預定的票排序,對於一種票,相當於求已知區間的補集,然後加入線段樹即可。

程式碼:

#include <iostream>
#include <cmath>
#include <cstdio>
#include <algorithm>

const int maxn=2e5+7;

using namespace std;

int n,m,k,q,x,y,ans,j;
int t[maxn*4],f[maxn][20];

struct node{
    int
l,r,k; }a[maxn]; bool cmp(node a,node b) { if (a.k==b.k) return a.l<b.l; return a.k<b.k; } void ins(int p,int l,int r,int x,int y,int k) { if ((l==x) && (r==y)) { t[p]=max(t[p],k); return; } int mid=(l+r)/2; if (y<=mid) ins(p*2,l,mid,x,y
,k); else if (x>mid) ins(p*2+1,mid+1,r,x,y,k); else { ins(p*2,l,mid,x,mid,k); ins(p*2+1,mid+1,r,mid+1,r,k); } } int getmax(int p,int l,int r,int x) { if (l==r) return t[p]; int mid=(l+r)/2; int tmp; if (x<=mid) tmp=getmax(p*2,l,mid,x); else tmp=getmax(p*2+1,mid+1,r,x); return max(tmp,t[p]); } int main() { freopen("trip.in","r",stdin); freopen("trip.out","w",stdout); scanf("%d%d%d",&n,&m,&k); for (int i=1;i<=m;i++) scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].k); sort(a+1,a+m+1,cmp); for (int i=1;i<=n;i++) ins(1,1,n,i,i,i); j=1; for (int i=1;i<=k;i++) { int r=1; while (a[j].k==i) { if (r<a[j].l) ins(1,1,n,r,a[j].l,a[j].l); r=max(r,a[j].r); j++; } ins(1,1,n,r,n,n); } for (int i=1;i<=n;i++) f[i][0]=getmax(1,1,n,i); for (int j=1;j<20;j++) { for (int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1]; } scanf("%d",&q); for (int i=1;i<=q;i++) { scanf("%d%d",&x,&y); if (f[x][19]<y) printf("-1\n"); else { int k=19; ans=0; while (k>=0) { if (f[x][k]<y) { x=f[x][k]; ans+=(1<<k); } k--; } printf("%d\n",ans+1); } } }