【題解】 Luogu CF375D Tree and Queries
阿新 • • 發佈:2018-11-13
原題傳送門
莫隊好題
我一上來想寫線段樹,隨後覺得不好寫並棄坑
我們可以看見沒有修改操作,欽定莫隊
但這是在樹上,所以不能直接用莫隊(廢話)
我們要樹鏈剖分,使得節點和節點的子樹能在一個區間裡(不會樹鏈剖分的出門左轉洛咕樹鏈剖分模板)
剩下的就是最基礎的莫隊,但是前置和後置++,--要注意qaq,我以前寫莫隊經常因為++,--的問題出鍋qaq
剩下一些細節見程式
#pragma GCC optimize("O3") #include <bits/stdc++.h> #define N 100005 using namespace std; inline int read() //io優化 { register int x=0,f=1;register char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f; } inline void write(register int x) { if(!x)putchar('0');if(x<0)x=-x,putchar('-'); static int sta[36];int tot=0; while(x)sta[tot++]=x%10,x/=10; while(tot)putchar(sta[--tot]+48); } struct edge{ int to,next; }e[N<<1]; int head[N],cnt; struct query{ int l,r,id,bl,k; }q[N]; int n,m,blocksize; int c[N]; int in[N],out[N],w[N]; inline void dfs(register int x,register int fa) { in[x]=++cnt; w[cnt]=c[x]; for (register int i=head[x];i;i=e[i].next) { int y=e[i].to; if (y==fa) continue; dfs(y,x); } out[x]=cnt; } inline bool cmp(register query a,register query b) { return a.bl==b.bl?a.r<b.r:a.bl<b.bl; } int ans[N],f[N],res[N]; //f[i]表示當前顏色為i的節點的個數,ans[i]表示當前出現次數大於等於i的顏色數量,res是最後的結果 bool inq[N]; inline void update(register int x) { if(inq[x]) --ans[f[w[x]]--]; else ++ans[++f[w[x]]]; inq[x]^=1; } int main() { n=read(),m=read(); for(register int i=1;i<=n;++i) c[i]=read(); for(register int i=1;i<n;++i) { int x=read(),y=read(); //鏈式前向星建圖 e[++cnt]=(edge){y,head[x]}; head[x]=cnt; e[++cnt]=(edge){x,head[y]}; head[y]=cnt; } cnt=0; dfs(1,1); //樹剖 blocksize=sqrt(n); //莫隊塊的大小 for(register int i=1;i<=m;++i) { int v=read(),k=read(); q[i]=(query){in[v],out[v],i,(in[v]-1)/blocksize+1,k}; } sort(q+1,q+1+m,cmp); int l=1,r=0; for(register int i=1;i<=m;++i) { int ll=q[i].l,rr=q[i].r; while(l<ll) update(l++); while(l>ll) update(--l); while(r>rr) update(r--); while(r<rr) update(++r); res[q[i].id]=ans[q[i].k]; } for(register int i=1;i<=m;++i) { write(res[i]); printf("\n"); } return 0; }