bzoj 3757: 蘋果樹(樹上莫隊)

來源:互聯網
上載者:User

標籤:

3757: 蘋果樹Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 1327  Solved: 510
[Submit][Status][Discuss]Description

    神犇家門口種了一棵蘋果樹。蘋果樹作為一棵樹,當然是呈樹狀結構,每根樹枝串連兩個蘋果,每個蘋果都可以沿著一條由樹枝構成的路徑連到樹根,而且這樣的路徑只存在一條。由於這棵蘋果樹是神犇種的,所以蘋果都發生了變異,變成了各種各樣的顏色。我們用一個1到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<=N
N<=50000
1<=U,V,Coli<=N
M<=100000
Source

[Submit][Status][Discuss]

題解:樹上莫隊。

首先要對樹上的點進行分塊,分塊方式與上一題bzoj 1086 相同。

然後對於每個詢問以左端點所在的塊為第一關鍵字,右端點的dfs序為第二關鍵字排序。

那麼如何進行區間的轉移呢?

網上有個神奇的證明:

用S(v, u)代表 v到u的路徑上的結點的集合。用root來代表根結點,用lca(v, u)來代表v、u的最近公用祖先。那麼S(v, u) = S(root, v) xor S(root, u) xor lca(v, u)其中xor是集合的對稱差。簡單來說就是節點出現兩次消掉。
lca很討厭,於是再定義T(v, u) = S(root, v) xor S(root, u)  //表示的就是u到v除去lca的路徑觀察將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)(有公式恐懼症的不要走啊 T_T)
也就是說,更新的時候,xor T(curV, targetV)就行了。即,對curV到targetV路徑(除開lca(curV, targetV))上的結點,將它們的存在性取反即可。(做的時候維護T,然後計算的時候在加上LCA就可以了)

知道這個T(targetV, curU)= T(curV, curU) xor T(curV, targetV)結論就可以每次進行轉移了。

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#define N 50003using namespace std;int n,m,sum,top;int c[N],dfsn[N],v[N*2],next[N*2],point[N],deep[N],root,sz,block;int fa[N][20],mi[20],vis[N],num[N],ans[N],cnt,tot,belong[N],st[N];struct data{int a,b,u,v,id;}q[100003];int cmp(data a,data b){if (belong[a.u]==belong[b.u]) return dfsn[a.v]<dfsn[b.v];return belong[a.u]<belong[b.u];}void add(int x,int y){tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y;tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x;}int  dfs(int x,int f){dfsn[x]=++sz;   int size=0;for (int i=1;i<=16;i++){if (deep[x]-mi[i]<0) break;fa[x][i]=fa[fa[x][i-1]][i-1];}for (int i=point[x];i;i=next[i]) if (v[i]!=f)  {  deep[v[i]]=deep[x]+1;  fa[v[i]][0]=x;  size+=dfs(v[i],x);  if (size>=block)//分塊  {  cnt++;  for (int j=1;j<=size;j++)   belong[st[top--]]=cnt;  size=0;  }  }st[++top]=x;return size+1;}int lca(int x,int y)//求最近公用祖先{if (deep[x]<deep[y]) swap(x,y);int k=deep[x]-deep[y];for (int i=0;i<17;i++)  if (k>>i&1)  x=fa[x][i];if (x==y) return x;for (int i=16;i>=0;i--) if (fa[x][i]!=fa[y][i])  x=fa[x][i],y=fa[y][i];return fa[x][0];}void reserve(int x)//將點的存在性取反{if (!vis[x]) { vis[x]=1; num[c[x]]++; if (num[c[x]]==1) sum++; }else{vis[x]=0; num[c[x]]--;if (num[c[x]]==0) sum--;}}void solve(int x,int y)//更改x,y路徑上除lca以外的點的存在性{while (x!=y){if (deep[x]>deep[y]) { reserve(x); x=fa[x][0]; }else{reserve(y); y=fa[y][0];}}}int main(){scanf("%d%d",&n,&m);for (int i=1;i<=n;i++) scanf("%d",&c[i]);for (int i=1;i<=n;i++) { int x,y; scanf("%d%d",&x,&y); if (!x) root=y;//因為0是沒有蘋果的,所有我們要尋找有蘋果的根 else if (!y)  root=x; else add(x,y); }mi[0]=1; block=sqrt(n);for (int i=1;i<=16;i++) mi[i]=mi[i-1]*2;deep[root]=1;dfs(root,0);++cnt;while (top)  belong[st[top--]]=cnt;for (int i=1;i<=m;i++) { scanf("%d%d%d%d",&q[i].u,&q[i].v,&q[i].a,&q[i].b); if (dfsn[q[i].u]>dfsn[q[i].v]) swap(q[i].u,q[i].v); q[i].id=i; }sort(q+1,q+m+1,cmp);    int t=lca(q[1].u,q[1].v);    solve(q[1].u,q[1].v);    reserve(t);    ans[q[1].id]=sum;    if (num[q[1].a]&&num[q[1].b]&&q[1].a!=q[1].b) ans[q[1].id]--;//色盲特判    reserve(t);    for (int i=2;i<=m;i++)    {    solve(q[i-1].u,q[i].u); solve(q[i-1].v,q[i].v);    int t=lca(q[i].u,q[i].v);    reserve(t);    ans[q[i].id]=sum;    if (num[q[i].a]&&num[q[i].b]&&q[i].a!=q[i].b) ans[q[i].id]--;    reserve(t);    }    for (int i=1;i<=m;i++)     printf("%d\n",ans[i]);}



bzoj 3757: 蘋果樹(樹上莫隊)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.