我是超链接
给定一棵n个点的树,节点带有权值,询问读入v,k,输出以v为根节点的子树有多少出现次数>=k的权值
这个子树一看就是dfs序啊,这个询问要分块啊,莫队排序似乎不错。
然后问题转化为【在一段已知区间内,出现次数在一段区间内,的数的个数】
之前做过一道题,当时的问题是【在一段已知区间内,数值在一段区间内,的种类数】,我们当时用的是权值分块
这一道题这么说来可以用次数分块,这样修改 O(1) O ( 1 ) ,查询 O(n−−√) O ( n )
#include
#include
#include
#include
using namespace std;
const int N=100005;
int tot,nxt[N*2],point[N],v[N*2],block,n,in[N],dfn[N],out[N],a[N],nn,pos[N],have[N],cnt[N],he[N],ans[N];
struct hh{int v,k,id,l,r;}q[N];
int cmp(hh a,hh b){return pos[a.l]<pos[b.l] || (pos[a.l]==pos[b.l] && a.rint x,int y)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void dfs(int x,int fa)
{
dfn[++nn]=x; in[x]=nn;
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa) dfs(v[i],x);
out[x]=nn;
}
void change(int x,int v)
{
int t=a[dfn[x]];
if (cnt[t]) have[cnt[t]]--,he[pos[cnt[t]]]--;
cnt[t]+=v;
if (cnt[t]) have[cnt[t]]++,he[pos[cnt[t]]]++;
}
int qurry(int x,int y)
{
if (x>y) return 0;int ans=0;
if (pos[x]==pos[y]) for (int i=x;i<=y;i++) ans+=have[i];
else
{
int up=min(n,pos[x]*block);
for (int i=x;i<=up;i++) ans+=have[i];
up=(pos[y]-1)*block+1;
for (int i=up;i<=y;i++) ans+=have[i];
for (int i=pos[x]+1;i<pos[y];i++) ans+=he[i];
}
return ans;
}
int main()
{
int m;scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;iint x,y;scanf("%d%d",&x,&y);
addline(x,y);
}
dfs(1,0);
block=sqrt(n);
for (int i=1;i<=n;i++) pos[i]=(i-1)/block+1;
for (int i=1;i<=m;i++) scanf("%d%d",&q[i].v,&q[i].k),q[i].l=in[q[i].v],q[i].r=out[q[i].v],q[i].id=i;
sort(q+1,q+m+1,cmp);
int l=1,r=0;
for (int i=1;i<=m;i++)
{
while (l<q[i].l) change(l++,-1);
while (l>q[i].l) change(--l,1);
while (r<q[i].r) change(++r,1);
while (r>q[i].r) change(r--,-1);
ans[q[i].id]=qurry(q[i].k,n);
}
for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
}