题目大意:给一颗N个节点的树,每个节点上有一个颜色。给出Q组询问,每次询问一个子树上恰好出现K次的颜色的个数。
做法:
- 将树DFS一遍,记下时间戳,得到每个子树dfs序的对应区间,并将颜色离散化。
- 记下每个询问,将询问子树变成询问区间,将区间左端点除以n−−√排序,在同一块的询问按右端点排序。
- 接着按莫队算法移动区间,得到对应答案。
| 14356067 | 2015-08-05 | 14:44:27 | Accepted 4358 | 1248MS | 17660K | 2603 B | G++ | Kurama |
|---|
#pragma comment(linker, "/STACK:1024000000,1024000000")
//C++拓栈外挂
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <map>
#define MAXN 100001
#define MEM 90
//拓栈内存 90MB
using namespace std;
int n,k,T,qn,SQRT,w[MAXN],ans[MAXN],Tq[MAXN],Tj[MAXN];
//Tj用于莫队算法统计颜色数,ans保存答案
int first[MAXN],ct;
map<int,int>m;
//离散化颜色用
struct tree{int iT,oT;}t[MAXN];
//对应子树的端点
struct query{int q,NO;}q[MAXN];
//保存询问
struct edge
{
int v,next;
void add(int u,int _v)
{
v=_v;
next=first[u];
first[u]=ct++;
}
}e[MAXN*2];
bool cmp(const query &a,const query &b)
{
if(t[a.q].iT/SQRT == t[b.q].iT/SQRT)return t[a.q].oT<t[b.q].oT;
return t[a.q].iT/SQRT < t[b.q].iT/SQRT;
}
//排序比较函数
void dfs(int p)
{
t[p].iT=++T;
Tq[T]=m[w[p]]; //保存节点离散化后颜色对应的序号
for(int i=first[p];i;i=e[i].next)
if(!t[e[i].v].iT)
dfs(e[i].v);
t[p].oT=T;
}
//DFS记下时间戳
void init()
{
m.clear();
ct=1;
scanf("%d%d",&n,&k);
for(int i=1,cnt=0;i<=n;i++)
{
scanf("%d",&w[i]);
if(!m[w[i] ])m[w[i] ]=++cnt;//离散化颜色
Tj[i]=first[i]=t[i].iT=0;
}
for(int i=1,u,v;i<n;i++)
{
scanf("%d%d",&u,&v);
e[ct].add(u,v);
e[ct].add(v,u);
}
T=0;
dfs(1);
scanf("%d",&qn);
for(int i=0;i<qn;i++)
{
scanf("%d",&q[i].q);
q[i].NO=i;
}
//记下询问顺序
SQRT=sqrt(n+1);
sort(q,q+qn,cmp);
}
void solve()
{
int l=1,r=0,ANS=0,num;
for(int i=0;i<qn;i++)
{
if(r<t[q[i].q].oT)
{
++r;
for(;r<=t[q[i].q].oT;++r)
{
num=Tj[Tq[r] ];
if(num==k)ANS--;
if(num+1==k)ANS++;
Tj[Tq[r] ]++;
}
r=t[q[i].q].oT;
}
if(l>t[q[i].q].iT){
--l;
for(;l>=t[q[i].q].iT;--l)
{
num=Tj[Tq[l] ];
if(num==k)ANS--;else
if(num+1==k)ANS++;
Tj[Tq[l] ]++;
}
l=t[q[i].q].iT;
}
if(r>t[q[i].q].oT)
{
for(;r>t[q[i].q].oT;--r)
{
num=Tj[Tq[r] ];
if(num==k)ANS--;else
if(num-1==k)ANS++;
Tj[Tq[r] ]--;
}
}
if(l<t[q[i].q].iT)
{
for(;l<t[q[i].q].iT;++l)
{
num=Tj[Tq[l] ];
if(num==k)ANS--;else
if(num-1==k)ANS++;
Tj[Tq[l] ]--;
}
}
ans[q[i].NO]=ANS;
}
}
//莫队算法
extern int main2(void) __asm__ ("main2");
int main2()
{
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
init();
solve();
printf("%sCase #%d:\n",(i==1)?(""):("\n"),i);
for(int j=0;j<qn;j++)printf("%d\n",ans[j]);
}
exit(0);
}
//g++拓栈外挂
int main()
{
int size = MEM << 20;
char *p = (char *)malloc(size) + size;
__asm__ __volatile__(
"movq %0, %%rsp\n"
"pushq $exit\n"
"jmp main2\n"
:: "r"(p));
}

本文介绍了一种利用树形结构DFS遍历和颜色离散化技术解决子树中特定颜色出现次数的问题,通过莫队算法优化查询效率。详细阐述了颜色离散化、时间戳记录、区间查询与排序等关键步骤。

540

被折叠的 条评论
为什么被折叠?



