標籤:
3757: 蘋果樹Time Limit: 20 Sec Memory Limit: 256 MB
Submit: 1305 Solved: 503
[Submit][Status][Discuss]Description
神犇家門口種了一棵蘋果樹。蘋果樹作為一棵樹,當然是呈樹狀結構,每根樹枝串連兩個蘋果,每個蘋果都可以沿著一條由樹枝構成的路徑連到樹根,而且這樣的路徑只存在一條。由於這棵蘋果樹是神犇種的,所以蘋果都發生了變異,變成了各種各樣的顏色。我們用一個到n之間的正整數來表示一種顏色。樹上一共有n個果。蘋每個蘋果都被編了號碼,號碼為一個1到n之間的正整數。我們用0代表樹根。只會有一個蘋果直接根。
有許許多多的人來神犇家裡膜拜神犇。可神犇可不是隨便就能膜拜的。前來膜拜神犇的人需要正確回答一個問題,才能進屋膜拜神犇。這個問題就是,從樹上編號為u的蘋果出發,由樹枝走到編號為v的蘋果,路徑上經過的蘋果一共有多少種不同的顏色(包括蘋果u和蘋果v的顏色)?不過神犇注意到,有些來膜拜的人患有色盲症。具體地說,一個人可能會認為顏色a就是顏色b,那麼他們在數蘋果的顏色時,如果既出現了顏色a的蘋果,又出現了顏色b的蘋果,這個人只會算入顏色b,而不會把顏色a算進來。
神犇是一個好人,他不會強人所難,也就會接受由於色盲症導致的答案錯誤(當然答案在色盲環境下也必須是正確的)。不過這樣神犇也就要更改他原先數顏色的程式了。雖然這對於神犇來說是小菜一碟,但是他想考驗一下你。你能替神犇完成這項任務嗎?
Input輸入第一行為兩個整數n和m,分別代表樹上蘋果的個數和前來膜拜的人數。接下來的一行包含n個數,第i個數代表編號為i的蘋果的顏色Coli。接下來有n行,每行包含兩個數x和y,代表有一根樹枝串連了蘋果x和y(或者根和一個蘋果)。接下來有m行,每行包含四個整數u、v、a和b,代表這個人要數蘋果u到蘋果v的顏色種數,同時這個人認為顏色a就是顏色b。如果a=b=0,則代表這個人沒有患色盲症。Output
輸出一共m行,每行僅包含一個整數,代表這個人應該數出的顏色種數。
Sample Input5 3
1 1 3 3 2
0 1
1 2
1 3
2 4
3 5
1 4 0 0
1 4 1 3
1 4 1 2
Sample Output2
1
2
HINT0<=x,y,a,b<=NN<=500001<=U,V,Coli<=NM<=100000SourceSolution
樹上莫隊裸題,下面來說說樹上莫隊
序列上的莫隊沒什麼好說的,至於樹上莫隊,思想是一樣的
對樹DFS分塊(詳見 BZOJ-1086王室聯邦哦),然後對詢問排序,然後處理就好,那麼就是具體的實現了
至於樹分塊,就是按照DFS時間戳記去搞,所以詢問排序的第一關鍵字就是所在的塊,第二關鍵字就是時間戳記
至於如何把樹上的一條路徑,轉化為一端區間?
首先我們設$S(u,v)$表示$u-v$的路徑上的點集,$LCA(u,v)$表示兩點的最近公用祖先,$root$為樹根
$xor$為集合的對稱差,即 只屬於其中一個集合,而不屬於另一個集合的元素組成的集合
那麼發現$S(u,v)=S(root,u) xor S(root,v) xor LCA(u,v)$ (不懂見哦)
就是節點出現兩次消掉
再有$T(u,v)=S(root, v) xor S(root, u)$
觀察將詢問$curV$移動到$targetV$前後$T(curV, curU)$變化:
$T(curV, curU)=S(root, curV) xor S(root, curU)$
$T(targetV, curU)=S(root, targetV) xor S(root, curU)$
取對稱差:
$T(curV, curU) xor T(targetV, curU)= (S(root, curV) xor S(root, curU)) xor (S(root, targetV) xor S(root, curU))$
由於對稱差的交換律、結合律:
$T(curV, curU) xor T(targetV, curU)= S(root, curV) xorS(root, targetV)$
兩邊同時$xor T(curV, curU)$:
$T(targetV, curU)= T(curV, curU) xor S(root, curV) xor S(root, targetV)$
$T(targetV, curU)=T(curV, curU) xor T(curV, targetV)$
也就是說,更新的時候,$xor T(curV, targetV)$就行了。
即,對$curV$到$targetV$路徑(除開$LCA(curV, targetV))$上的結點,將它們的存在性取反即可。 ---------------------By VFK至於路徑上取對稱差?求$S(7,10)$,很顯然$root=1$,$S(1,7)=\left\{1,3,7\right\},S(1,10)=\left\{1,4,8,10\right\}$所以$S(1,7) xor S(1,10)=\left\{3,7,4,8,10\right\}$,$\left\{3,7,4,8,10\right\} xor LCA(7,10)=\left\{1,3,7,4,8,10\right\}=S(7,10)$圖中藍色的路徑表示$S(1,7)$,綠色路徑表示$S(1,10)$,紅色路徑表示$S(7,10)$,黃色圈表示$LCA(7,10)$其餘的都是一樣..詢問間的移動,原理一樣,感受一下就好Code
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;int read(){ int x=0;char ch=getchar(); while(ch<‘0‘||ch>‘9‘) ch=getchar(); while(ch>=‘0‘&&ch<=‘9‘) {x=x*10+ch-‘0‘;ch=getchar();} return x;}#define maxn 50100#define maxq 100100int n,m,fk,knum,ans,root; struct Edgenode{int to,next;}edge[maxn<<1];int head[maxn],cnt=1;void add(int u,int v){cnt++;edge[cnt].next=head[u];head[u]=cnt;edge[cnt].to=v;}void insert(int u,int v){add(u,v); add(v,u);}int deep[maxn],father[maxn][25],dfsx,stack[maxn],top,rt[maxn],an[maxq],p[maxn];struct Pointnode{int dfs,col;}po[maxn];struct Asknode{ int a,b,u,v,id; bool operator < (const Asknode & A) const { if(rt[u]==rt[A.u]) return po[v].dfs<po[A.v].dfs; else return rt[u]<rt[A.u]; }}q[maxq];bool visit[maxn];int DFS(int now){ int size=0; po[now].dfs=++dfsx; for (int i=1; i<=20; i++) if (deep[now]>=(1<<i)) father[now][i]=father[father[now][i-1]][i-1]; for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=father[now][0]) { deep[edge[i].to]=deep[now]+1; father[edge[i].to][0]=now; size+=DFS(edge[i].to); if (size>=fk) { knum++; for(int j=1; j<=size; j++) rt[stack[top--]]=knum; size=0; } } stack[++top]=now; return size+1;} int LCA(int x,int y){ if (deep[x]<deep[y]) swap(x,y); int dd=deep[x]-deep[y]; for (int i=0; i<=20; i++) if (dd&(1<<i) && dd>=(1<<i)) x=father[x][i]; for (int i=20; i>=0; i--) if (father[x][i]!=father[y][i]) x=father[x][i],y=father[y][i]; if (x==y) return x; else return father[x][0];}void reserv(int x){ if (!visit[x]) {visit[x]=1; p[po[x].col]++; if (p[po[x].col]==1) ans++;} else {visit[x]=0; p[po[x].col]--; if (p[po[x].col]==0) ans--;}}void work(int u,int v){ while (u!=v) if (deep[u]>deep[v]) reserv(u),u=father[u][0]; else reserv(v),v=father[v][0];}int main(){ n=read(),m=read(); fk=sqrt(n); for (int i=1; i<=n; i++) po[i].col=read(); for (int u,v,i=1; i<=n; i++) { u=read(),v=read(); if (!u) root=v; else if (!v) root=u; else insert(u,v); } DFS(root); knum++; while (top) rt[stack[top--]]=knum; for (int i=1; i<=m; i++) { q[i].u=read();q[i].v=read();q[i].a=read();q[i].b=read();q[i].id=i; if (po[q[i].u].dfs>po[q[i].v].dfs) swap(q[i].u,q[i].v); } sort(q+1,q+m+1); int T=LCA(q[1].u,q[1].v); work(q[1].u,q[1].v); reserv(T); an[q[1].id]=ans; if (p[q[1].a] && p[q[1].b] && q[1].a!=q[1].b) an[q[1].id]--; reserv(T); for (int i=2; i<=m; i++) { work(q[i-1].u,q[i].u); work(q[i-1].v,q[i].v); T=LCA(q[i].u,q[i].v); reserv(T); an[q[i].id]=ans; if(p[q[i].a] && p[q[i].b] && q[i].a!=q[i].b) an[q[i].id]--; reserv(T); } for (int i=1; i<=m; i++) printf("%d\n",an[i]); return 0;}
【BZOJ-3757】蘋果樹 塊狀樹 + 樹上莫隊