【NOIP2017模擬8.8】Trip
阿新 • • 發佈:2017-08-09
關系 pan 根據 大於 二叉 計算 我們 else freopen
考慮30分的可以拿個單調棧向左掃一遍向右掃一遍再將個數加起來再減去1即可。
這道題是要求我們判斷這個景點的評估值在給定的子區間裏是否有比它大值存在,如果一邊沒有大於它的存在,則它就是旅客會前往的景點。
既需要位置關系又需要大小關系,我們考慮大根笛卡爾樹。
笛卡爾樹是一種同時滿足二叉搜索樹和堆的性質的數據結構。
它的中序遍歷的序列為原數組序列。
樹中節點的值大於其左右子節點的值。
建樹很簡單, 用個單調棧O(n)即可建好。我們很容易發現一些性質:
1.對於一個詢問L,R它的答案只會出現在笛卡爾樹的路徑上。
2.L,R的LCA中,從L到LCA路徑上,有一個點是其父親的左孩子答案就加一,R到LCA上,有一個點是其父親的右孩子答案就加一。
我們知道笛卡爾樹上一個點A是其父親B的左孩子表明A在它父親B的左邊,且權值小於父親B,對於L到LCA路徑(也就是區間L-LCA)中而言這意味著A的左邊沒有比它大的點(如果有,那麽A應該會在比它大的那個點C的右邊),於是就對答案有1的貢獻,雖然右邊有比它大的點(父親B);對於R到LCA路徑(也就是區間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