1. 程式人生 > 其它 >Nginx跨域瞭解及模擬和解決

Nginx跨域瞭解及模擬和解決

前置知識:
倍增LCA

樹上差分

可持久化線段樹(動態開點)

定義:

線段樹合併就是合併值域相同的線段樹。

非常的好理解。Code:

 1 int merge(int p,int q,int l,int r){//在這裡,我們選擇將兩棵樹合併到第一棵樹中
 2     if(!q) return p;
 3     if(!p) return q;//因為使用動態開點線段樹,所以當其中一棵線段樹無該節點是便可停止
 4     if(l==r){
 5         t[p].maxn+=t[q].maxn;
 6         if(t[p].maxn>0) t[p].pos=l;
 7
else t[p].pos=0; 8 return p; 9 } 10 int mid=(l+r)/2; 11 t[p].l=merge(t[p].l,t[q].l,l,mid); 12 t[p].r=merge(t[p].r,t[q].r,mid+1,r); 13 pushup(p); 14 return p; 15 }

懂不起?那確實。

讓我們來看一道例題來感受線段樹合併的用途。

P4556 [Vani有約會]雨天的尾巴 /【模板】線段樹合併

思路:

對於每個節點,我們用動態開點線段樹來記錄每種救濟糧的個數。

那這道題和線段樹合併

有什麼關係呢?

要合併,就需要在統計答案時進行“”的操作。

於是我們就想到了差分

樹+差分=樹上差分

於是思路就出來了:

用樹上差分維護n棵線段樹,統計答案時利用線段樹合併完成。

詳細思路在程式碼中解釋:

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int N=1e5+5;
  4 const int M=6e6+5; 
  5 int n,m; 
  6 int a,b;
  7 int x,y,z;
  8 int T;
  9 int idx=0,tot=0;
 10 int f[N][30],d[N],root[N],head[N];
11 int ans[N]; 12 struct node{ 13 int v,next; 14 }s[2*N]; 15 struct node1{ 16 int l,r,maxn,pos; 17 //maxn用於記錄該區間最多的飼料數 18 //pos用於記錄該區間最多的飼料的種類 19 }t[M]; 20 21 void build(int a,int b){//建圖 22 s[++idx].v=b; 23 s[idx].next=head[a]; 24 head[a]=idx; 25 } 26 27 void bfs(){//倍增求LCA的預處理 28 memset(d,0,sizeof(d)); 29 d[1]=1; 30 queue<int> q; 31 q.push(1); 32 while(!q.empty()){ 33 int x=q.front(); 34 q.pop(); 35 for(int i=head[x];i;i=s[i].next){ 36 int y=s[i].v; 37 if(d[y]) continue; 38 d[y]=d[x]+1; 39 f[y][0]=x; 40 for(int i=1;i<=T;i++) f[y][i]=f[f[y][i-1]][i-1]; 41 q.push(y); 42 } 43 } 44 return ; 45 } 46 47 int LCA(int x,int y){//求LCA 48 if(d[x]<d[y]) swap(x,y); 49 for(int i=T;i>=0;i--){ 50 if(d[f[x][i]]>=d[y]) x=f[x][i]; 51 } 52 if(x==y) return x; 53 for(int i=T;i>=0;i--){ 54 if(f[x][i]!=f[y][i]){ 55 x=f[x][i]; 56 y=f[y][i]; 57 } 58 } 59 return f[x][0]; 60 } 61 62 int Build(){//動態開點 63 tot++; 64 t[tot].l=t[tot].r=t[tot].maxn=t[tot].pos=0; 65 return tot; 66 } 67 68 void pushup(int p){//更新最大飼料數量及種類 69 if(t[t[p].l].maxn>=t[t[p].r].maxn){//注意:因為數量相同時,編號小的優先,所以是>=而不是> 70 t[p].maxn=t[t[p].l].maxn; 71 t[p].pos=t[t[p].l].pos; 72 } 73 else{ 74 t[p].maxn=t[t[p].r].maxn; 75 t[p].pos=t[t[p].r].pos; 76 } 77 return ; 78 } 79 80 void modify(int p,int l,int r,int x,int v){//單點修改 81 if(l==r){//因為用了樹上差分,所以區間和可能是負數 82 t[p].maxn+=v; 83 if(t[p].maxn>0) t[p].pos=x; 84 else t[p].pos=0; 85 return ; 86 } 87 int mid=(l+r)/2; 88 if(x<=mid){ 89 if(!t[p].l) t[p].l=Build();//記住!動態開點! 90 modify(t[p].l,l,mid,x,v); 91 } 92 else{ 93 if(!t[p].r) t[p].r=Build();//記住!動態開點! 94 modify(t[p].r,mid+1,r,x,v); 95 } 96 pushup(p); 97 return ; 98 } 99 100 int merge(int p,int q,int l,int r){//線段樹合併 101 if(!q) return p; 102 if(!p) return q; 103 if(l==r){ 104 t[p].maxn+=t[q].maxn; 105 if(t[p].maxn>0) t[p].pos=l; 106 else t[p].pos=0; 107 return p; 108 } 109 int mid=(l+r)/2; 110 t[p].l=merge(t[p].l,t[q].l,l,mid); 111 t[p].r=merge(t[p].r,t[q].r,mid+1,r); 112 pushup(p); 113 return p; 114 } 115 116 void dfs(int x){ 117 if(!root[x]) root[x]=Build();//記住!動態開點! 118 for(int i=head[x];i;i=s[i].next){ 119 int y=s[i].v; 120 if(f[x][0]==y) continue; 121 dfs(y); 122 root[x]=merge(root[x],root[y],1,N);//注意:此處為root[x]而非x是因為對於原樹的每一個節點都是線段樹的根節點 123 } 124 ans[x]=t[root[x]].pos; 125 return ; 126 } 127 128 int main(){ 129 cin>>n>>m; 130 T=(log(n)/log(2))+1; 131 for(int i=1;i<n;i++){ 132 cin>>a>>b; 133 build(a,b); 134 build(b,a); 135 } 136 bfs();//預處理 137 for(int i=1;i<=m;i++){ 138 cin>>x>>y>>z; 139 int lc=LCA(x,y); 140 if(!root[x]) root[x]=Build(); 141 modify(root[x],1,N,z,1); 142 if(!root[y]) root[y]=Build(); 143 modify(root[y],1,N,z,1); 144 if(!root[lc]) root[lc]=Build(); 145 modify(root[lc],1,N,z,-1); 146 if(!root[f[lc][0]]) root[f[lc][0]]=Build(); 147 modify(root[f[lc][0]],1,N,z,-1);//樹上差分 148 } 149 dfs(1);//答案 150 for(int i=1;i<=n;i++) cout<<ans[i]<<endl; 151 return 0; 152 }

這樣複雜度是多少呢?

由於是動態開點線段樹,所以基於總運算元m,每次操作至多新建一條長為log v的鏈,所以至多隻有O(mlogv)的節點。

那麼合併時,遍歷了線段樹中所有已有的節點。

那麼這樣合併複雜度也是O(mlogv)