給定一棵 \(n\) 個點的無根樹, 點帶權. \(q\) 次詢問, 每次給定樹上的若干路徑, 求這些路徑上的點共有多少種不一樣權值以及這些點的權值組成的集合的 \(\operatorname{mex}\).php
\(n,q\le1\times 10^5,v_i\le30000\). 部分測試點強制在線.c++
首先吐槽一下沙雕考試出題人又出原題git
這個只有 \(30000\) 的值域明示要用 std::bitset
趴...測試
而後發現這個 std::bitset
的標準接口不滋磁查 lowbit
, 因而 \(\operatorname{mex}\) 就無法求了...(因而沙雕rvalue就手寫了一個)ui
而後按照套路樹剖一下把樹鏈拆成 DFS 序上的 \(\log\) 段區間, 而後變成求若干段區間的 bitset
的並. 發現數據範圍恰好容許開 \(O(n)\) 個 bitset
, 那麼直接對 DFS 序分塊, 而後把一段連續的塊的 bitset
都求出來, 這樣查詢的時候就能夠只處理兩端的散塊了.this
對於一次查詢, 首先樹剖出若干個區間, 而後發現這一坨區間可能有不少重複, 咱們隨手排個序把相交的區間都並起來. 直接在預處理好的連續塊數據上增量構造就能夠了.spa
時間複雜度好像是 \(O\left(n\left(\sqrt n+\frac V w\right)+q\left(\sqrt n\log n+\frac V w\right)\right)\). 實測跑得巨快無比. 大概是由於複雜度中 \(O(q \sqrt n \log n)\) 的部分由於沒法造出每條樹鏈長度都達到 \(O(\sqrt n)\) 的同時剖出來的樹鏈數量達到 \(\log n\) 的路徑而跑不滿趴(樹鏈數量增長必然致使整樹深度變淺)...神仙 hzoizcl 說這個那個 \(O(\sqrt n \log n)\) 實際只能達到 \(O\left(\log\left (\sqrt n^{\sqrt n}\right)\right)\) 級別qaq...(這個值大概是 \(2\texttt{k}\) 左右因而複雜度好像沒毛病)code
#include <bits/stdc++.h> const int SQRN=320; const int MAXV=1e5+10; const int MAXE=2e5+10; typedef unsigned int uint; struct Edge{ int from; int to; Edge* next; }; Edge E[MAXE]; Edge* head[MAXV]; Edge* topE=E; struct Bits{ static const int MAXL=940; uint data[MAXL]; Bits(){} inline void operator|=(const Bits& b){ for(int i=0;i<MAXL;i++) this->data[i]|=b.data[i]; } inline int count(){ int ans=0; for(int i=0;i<MAXL;i++) ans+=__builtin_popcount(this->data[i]); return ans; } inline int mex(){ for(int i=0;i<MAXL;i++){ if(__builtin_popcount(this->data[i])!=32){ for(int j=0;j<32;j++) if(((this->data[i]>>j)&1)==0) return (i<<5)|j; } } return MAXL<<5; } inline void set(int p){ this->data[p>>5]|=(1u<<(p&31)); } }; Bits s[SQRN][SQRN]; Bits ans; int n; int q; int clk; int sqrn; int blk[MAXV]; int dfn[MAXV]; int val[MAXV]; int pos[MAXV]; int prt[MAXV]; int top[MAXV]; int son[MAXV]; int deep[MAXV]; int size[MAXV]; int cur; std::pair<int,int> R[MAXV]; void Merge(); void DFS(int,int); void Export(int,int); inline int ReadInt(); void DFS(int,int,int); inline void Insert(int,int); int main(){ int T; n=ReadInt(),q=ReadInt(),T=ReadInt(); sqrn=sqrt(n+0.5); for(int i=1;i<=n;i++) val[i]=ReadInt(); for(int i=1;i<n;i++){ int a=ReadInt(),b=ReadInt(); Insert(a,b); Insert(b,a); } DFS(1,0,0); DFS(1,1); for(int i=0;i*sqrn<n;i++){ for(int j=i;j*sqrn<n;j++){ if(j>i) s[i][j]=s[i][j-1]; for(int k=j*sqrn;blk[k]==j&&k<n;k++) s[i][j].set(val[pos[k]]); } } // Query; int lastans=0; for(int i=0;i<q;i++){ memset(ans.data,0,sizeof(ans.data)); int cnt=ReadInt(); cur=0; while(cnt--){ int a=ReadInt()^(lastans*T),b=ReadInt()^(lastans*T); Export(a,b); } Merge(); for(int i=0;i<cur;i++){ int l=R[i].first,r=R[i].second; if(blk[l]==blk[r]){ for(int i=l;i<=r;i++) ans.set(val[pos[i]]); } else{ if(blk[l]<blk[r]-1) ans|=s[blk[l]+1][blk[r]-1]; for(int i=l;blk[i]==blk[l];i++) ans.set(val[pos[i]]); for(int i=r;blk[i]==blk[r];i--) ans.set(val[pos[i]]); } // printf("%d %d %d\n",ans.data[0],ans.data[1],ans.data[2]); } int x=ans.count(),y=ans.mex(); printf("%d %d\n",x,y); lastans=x+y; } return 0; } void Export(int x,int y){ while(top[x]!=top[y]){ if(deep[top[x]]<deep[top[y]]) std::swap(x,y); R[cur++]=std::make_pair(dfn[top[x]],dfn[x]); x=prt[top[x]]; } if(deep[x]>deep[y]) std::swap(x,y); R[cur++]=std::make_pair(dfn[x],dfn[y]); } void Merge(){ int cnt=cur; std::sort(R,R+cnt); int l=R[0].first,r=R[0].second; for(int i=1;i<cnt;i++){ if(R[i].first>r){ R[cur++]=std::make_pair(l,r); l=R[i].first; r=R[i].second; } else r=std::max(r,R[i].second); } R[cur++]=std::make_pair(l,r); } void DFS(int root,int prt,int deep){ ::prt[root]=prt; ::deep[root]=deep; ::size[root]=1; for(Edge* i=head[root];i!=NULL;i=i->next){ if(i->to!=prt){ DFS(i->to,root,deep+1); size[root]+=size[i->to]; if(size[i->to]>size[son[root]]) son[root]=i->to; } } } void DFS(int root,int top){ ::dfn[root]=clk; ::pos[clk]=root; ::blk[clk]=clk/sqrn; ++clk; ::top[root]=top; if(son[root]) DFS(son[root],top); for(Edge* i=head[root];i!=NULL;i=i->next) if(i->to!=son[root]&&i->to!=prt[root]) DFS(i->to,i->to); } inline void Insert(int from,int to){ topE->from=from; topE->to=to; topE->next=head[from]; head[from]=topE++; } inline int ReadInt(){ int x=0; register char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)){ x=x*10+ch-'0'; ch=getchar(); } return x; }