1. 程式人生 > >【NOIP2017模擬8.8】Trip

【NOIP2017模擬8.8】Trip

關系 pan 根據 大於 二叉 計算 我們 else freopen

Description

多年之後,worldwideD厭倦競爭,隱居山林。
他的家鄉開始發展起了旅遊業,在一條很長的主幹道上,有N個旅遊景點,按順序編號為1到N。根據遊客們網上的評分,第i個景點有一個評估值a[i],為了區分開不同的景點,評估值是兩兩不同的。
今天有M組遊客前來旅遊,第i組遊客選擇遍歷景點Li到景點Ri這一段路。他們搜到Li到Ri的所有評估值,如果對於景點j(Li≤j≤Ri),不存在景點x(Li≤x<j)滿足a[x]>a[j]或不存在景點y(j<y≤Ri)滿足a[y]>a[j],那麽他們會進入景點j。
現在worldwideD想知道,每組遊客會去多少個景點。

Input

第一行兩個整數N,M,意義見題面。
接下來一行N個整數,第i個是a[i],意義見題面。
接下來M行,第i行兩個整數Li,Ri,意義見題目。

Output

M行,第i行表示第i組遊客去的景點個數。

Sample Input

6 4
3 1 7 4 5 2
1 5
2 5
2 6
4 6

Sample Output

3
3
4
3

Data Constraint

30%:N,M≤5,000
60%:N,M≤100,000
100%:N,M≤1,000,000 0≤|a[i]|≤1,000,000,000 1≤Li≤Ri≤N

Hint

第一組遊客選擇路段的景點評估值序列為[3,1,7,4,5],其中3,7,5滿足條件
第二組遊客選擇路段的景點評估值序列為[1,7,4,5],其中1,7,5滿足條件
第三組遊客選擇路段的景點評估值序列為[1,7,4,5,2],其中3,7,5,2滿足條件
第四組遊客選擇路段的景點評估值序列為[4,5,2],其中4,5,2滿足條件
本題數據規模較大,請註意您的常數造成的影響。
在這裏給出一種輸入輸出優化的模板,在主程序中直接調用read()即可讀入一個整數,調用write(x)可以把一個int變量x輸出並換行。
int read()
{
int x=0,sig=1;
char c;
for (c=getchar();c<‘0‘ || c>‘9‘;c=getchar()) if (c==‘-‘) sig=-1;
for (;c>=‘0‘ && c<=‘9‘;c=getchar()) x=x*10+c-48;
return x*sig;
}
void write(int x)
{
if (!x) putchar(‘0‘);else
{
char s[10];
int i,j=0;
for (;x>0;x/=10) s[j++]=x%10;
for (i=j-1;i>=0;i--) putchar(s[i]+48);
}
putchar(‘\n‘);
}

考慮30分的可以拿個單調棧向左掃一遍向右掃一遍再將個數加起來再減去1即可。

這道題是要求我們判斷這個景點的評估值在給定的子區間裏是否有比它大值存在,如果一邊沒有大於它的存在,則它就是旅客會前往的景點。

既需要位置關系又需要大小關系,我們考慮大根笛卡爾樹。

笛卡爾樹是一種同時滿足二叉搜索樹和堆的性質的數據結構。

它的中序遍歷的序列為原數組序列。

樹中節點的值大於其左右子節點的值。

建樹很簡單, 用個單調棧O(n)即可建好。我們很容易發現一些性質:

1.對於一個詢問L,R它的答案只會出現在笛卡爾樹的路徑上。

2.L,R的LCA中,從L到LCA路徑上,有一個點是其父親的左孩子答案就加一,R到LCA上,有一個點是其父親的右孩子答案就加一。

我們知道笛卡爾樹上一個點A是其父親B的左孩子表明A在它父親B的左邊,且權值小於父親B,對於LLCA路徑(也就是區間L-LCA)中而言這意味著A的左邊沒有比它大的點(如果有,那麽A應該會在比它大的那個點C的右邊),於是就對答案有1的貢獻,雖然右邊有比它大的點(父親B);對於RLCA路徑(也就是區間LCA-R)中則相反。這就很好的符合題目的性質,我們就可以用笛卡爾樹解決這道題了。

我們就可以用tarjan求出LCA,然後預處理下每個點到根節點的路徑上有多少個點是其父親的左孩子和右孩子,然後計算出答案即可。時間復雜度 O(NαN)

技術分享
  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 #define N 3000002
  5 using namespace std;
  6 int n,m,t,zhan[N],len,num,ans[N],f[N],u,v,head[N],root,visit[N];
  7 struct data1{
  8     int l,r,p,v,lnum,rnum;
  9     void init(){
 10         l=0,r=0,p=0,v=0,lnum=0,rnum=0;
 11     }
 12 }tree[N];
 13 struct data2{
 14     int next,to,sign;
 15 }line[N];
 16 int read()
 17 {
 18        int x=0,sig=1;
 19        char c;
 20        for (c=getchar();c<0 || c>9;c=getchar()) if (c==-) sig=-1;
 21        for (;c>=0 && c<=9;c=getchar()) x=x*10+c-48;
 22        return x*sig;
 23 }
 24 void write(int x)
 25 {
 26        if (!x) putchar(0);else
 27        {
 28               char s[10];
 29               int i,j=0;
 30               for (;x>0;x/=10) s[j++]=x%10;
 31               for (i=j-1;i>=0;i--) putchar(s[i]+48);
 32        }
 33        putchar(\n);
 34 }
 35 void add(int u,int v){
 36     num++;
 37     line[num].next=head[u];
 38     line[num].to=v;
 39     line[num].sign=num;
 40     head[u]=num;
 41     num++;
 42     line[num].next=head[v];
 43     line[num].to=u;
 44     line[num].sign=num;
 45     head[v]=num;
 46 }
 47 int build(){     //建笛卡爾樹 
 48     len=1;
 49     zhan[1]=1;
 50     for (int i=2;i<=n;i++){
 51         while ((len>0)&&(tree[zhan[len]].v<tree[i].v)) len--;
 52         if (len){
 53             tree[i].p=zhan[len];
 54             tree[tree[zhan[len]].r].p=i;
 55             tree[i].l=tree[zhan[len]].r;
 56             tree[zhan[len]].r=i;
 57         }
 58         else {
 59             tree[zhan[1]].p=i;
 60             tree[i].l=zhan[1];
 61         }
 62         zhan[++len]=i;    
 63     }
 64     return zhan[1];
 65 }
 66 int find(int x){
 67     if (f[x]==x) return x;
 68     f[x]=find(f[x]);
 69     return f[x];
 70 }
 71 void tarjan(int x,int ln,int rn){   //Tarjan求LCA 
 72     f[x]=x;
 73     visit[x]=1;
 74     tree[x].lnum=ln;
 75     tree[x].rnum=rn;
 76     if (tree[x].l) {
 77         tarjan(tree[x].l,ln+1,rn);
 78         f[tree[x].l]=x;
 79     }
 80     if (tree[x].r) {
 81         tarjan(tree[x].r,ln,rn+1);
 82         f[tree[x].r]=x;
 83     }
 84     int v=0,e=0;
 85     for (int i=head[x];i!=0;i=line[i].next){
 86         v=line[i].to;
 87         if (visit[v]){e=find(v);
 88         if (line[i].sign&1) ans[(line[i].sign+1)/2]=tree[x].lnum-tree[e].lnum+tree[v].rnum-tree[e].rnum+1;
 89         else ans[line[i].sign/2]=tree[v].lnum-tree[e].lnum+tree[x].rnum-tree[e].rnum+1;
 90         }
 91     }
 92 }
 93 int main(){
 94     freopen("trip.in","r",stdin);
 95     freopen("trip.out","w",stdout);    
 96     memset(visit,0,sizeof(visit));
 97     n=read();
 98     m=read();
 99     for (int i=1;i<=n;i++){
100         tree[i].init();
101         tree[i].v=read();
102     }
103     root=build();
104     num=0;
105     for (int i=1;i<=m;i++){
106         u=read();
107         v=read();
108         add(u,v);
109     }
110     tarjan(root,0,0);
111     for (int i=1;i<=m;i++)
112      write(ans[i]);
113     return 0;
114 }
神奇的代碼

聯想很重要

【NOIP2017模擬8.8】Trip