http://acm.hdu.edu.cn/showproblem.php?pid=4358
题意:
给你一颗树,n个节点,每个有其权值。给一个k。
q次询问,每次询问 以x为根节点的子树里,有多少种权值恰好出现次数为k。
我们先求个dfs序,把树型结构转为线性数组。
那么题目变成q次查询,每次查询区间L【x】,R【x】之间有多少个权值,出现的次数恰好为k
而本题可以用离线的做法,先把所有查询的区间按右端点排序。
pos[x][j],代表值为x的数在J次出现的位置
接下来是用dfs序数组建树的过程,显然当我们用1-i 的节点建树之后,i+1之后的节点不会影响 所有【 以i为右端点的区间查询】
因此我们 边插入 边查询【j,i】(j<=i) 的答案
插入一个x,假设其出现次数为y,当y<k时,显然对答案无影响,当y>=k时
会影响的是(pos[x][y-k],pos[x][y-k+1]】这个区间所有的数,显然这个区间的任意点z,构成的区间【z,当前最右边】这部分的答案会+1,
同时,当y>k的情况下,还会导致【p[x][y-k-1],p[x][y-k]】这个区间的任意点z,构成的区间【z,到最右边】这部分答案-1 (因为y超过了k嘛)
因此我们从左到右 依次把dfs序数组的数插进树状数组,每次插入后,可以查询到【j,i】的正确答案,插入n次的过程可以查到所有区间的询问。
。。对区间+1 -1部分直接用线段树的区间更新比较好写咯
线段树写法:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <vector>
#define inf 0x7fffffff
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 50005;
using namespace std;
int n,m;
const int N = 100005 ;
struct tree
{
int sum[4*N], add[4*N] ;
void pushDown(int i, int l, int r)
{
if(add[i] != 0)
{
int mid = (l + r) >> 1;
add[i << 1] += add[i];
sum[i << 1] += (mid - l + 1) * add[i]; //[l, mid]代表左儿子区间
add[i << 1 | 1] += add[i];
sum[i << 1 | 1] += (r - mid) * add[i]; //[mid + 1, r]代表右儿子区间
add[i] = 0;
}
}
void update(int i, int l, int r, int ql, int qr, int val) //更新区间为qlqr,当前区间为l,r,代表当前区间和的节点为i,更新值为val,
{
if(l > qr || ql > r)
return ;
if(l >= ql && r <= qr)
{
sum[i] += (r - l + 1) * val;
add[i] += val;
return ;
}
pushDown(i, l, r);
int mid = (l + r) >> 1;
update(i << 1, l, mid, ql, qr, val);
update(i << 1 | 1, mid + 1, r, ql, qr, val);
sum[i] = sum[i << 1] + sum[i << 1 | 1];
}
int query(int i, int l, int r, int ql, int qr) //查询区间为qlqr,当前区间为l,r,代表当前区间和的节点为i
{
if(l > qr || ql > r)
return 0;
if(l >= ql && r <= qr)
return sum[i];
pushDown(i, l, r);
int mid =( l + r) >> 1;
return query(i << 1, l, mid, ql, qr)
+ query(i << 1 | 1, mid + 1, r, ql, qr);
}
};
tree tp;
vector < vector<int> > mp(100000+50);
int id;
int in[100000+50],out[100000+50];
int ren[100005];
int vis[100005];
void dfs1(int x)
{
in[x]=++id;
ren[id]=x;
vis[x]=1;
int i;
for (i=0; i<mp[x].size(); i++)
{
int v=mp[x][i];
if (vis[v])continue;
dfs1(v);
}
out[x]=id;
}
int dis[100005];
int aa[100005];
vector<int>pos[100005];
struct node
{
int x,y,id;
node() {}
node(int a,int b)
{
x=a,y=b;
}
};
node atm[100005];
bool cmp(node a,node b)
{
return a.y<b.y;
}
int ans[100005];
int main()
{
int t;
cin>>t;
int cnt=1;
while(t--)
{
int k,i,j;
int x,y;
memset(vis,0,sizeof vis);
memset(tp.sum,0,sizeof tp.sum);
memset(tp.add,0,sizeof tp.add);
id=0;
for (i=1; i<=n; i++) mp[i].clear();
cin>>n>>k;
for (i=1; i<=n; i++)
scanf("%d",&aa[i]),dis[i]=aa[i];
sort(dis+1,dis+1+n);
int sz=unique(dis+1,dis+1+n)-dis-1;
for (i=1; i<=sz; i++) pos[i].clear(),pos[i].push_back(0); //初始化第0个位置
for (i=1; i<=n-1; i++)
{
scanf("%d%d",&x,&y);
mp[x].push_back(y);
mp[y].push_back(x);
}
dfs1(1); //求dfs序
int q;
cin>>q;
for ( i=1; i<=q; i++)
{
scanf("%d",&x);
atm[i].x=in[x]; //管辖区间范围
atm[i].y=out[x];
atm[i].id=i;
}
sort(atm+1,atm+1+n,cmp);
j=1;
for ( i=1; i<=n; i++)
{
x=ren[i ];
x=lower_bound(dis+1,dis+1+sz,aa[x])-dis; //离散化对应下标
pos[x].push_back(i);
y=pos[x].size()-1; //出现过y次
if (y>=k)
{
tp.update(1,1,n,pos[x][y-k]+1,pos[x][y-k+1],1);
if (y>k) //答案-1的区间
{
tp.update(1,1,n,pos[x][y-k-1]+1,pos[x][y-k],-1);
}
}
for (; j<=q&&atm[j].y==i; j++) //处理i为右端点的询问
ans[atm[j].id]=tp.query(1,1,n,atm[j].x,atm[j].x);//printf("%d",tp.get(atm[j].x));
}
printf("Case #%d:\n",cnt++);
for (i=1; i<=q; i++)
printf("%d\n",ans[i]);
if (t)printf("\n");
}
return 0;
}
树状数组写法:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <vector>
#define inf 0x7fffffff
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 50005;
using namespace std;
int n,m;
struct tree
{
int tree[100000+5];
int lowbit(int x)
{
return x&-x;
}
void add(int x,int value)
{
for (int i=x; i<=n; i=i+lowbit(i))
{
tree[i]+=value;
}
}
int get(int x)
{
int sum=0;
for (int i=x; i; i-=lowbit(i))
{
sum+=tree[i];
}
return sum;
}
};
tree tp;
vector < vector<int> > mp(100000+50);
int id;
int in[100000+50],out[100000+50];
int ren[100005];
int vis[100005];
void dfs1(int x)
{
in[x]=++id;
ren[id]=x;
vis[x]=1;
int i;
for (i=0; i<mp[x].size(); i++)
{
int v=mp[x][i];
if (vis[v])continue;
dfs1(v);
}
out[x]=id;
}
int dis[100005];
int aa[100005];
vector<int>pos[100005];
struct node
{
int x,y,id;
node(){}
node(int a,int b)
{
x=a,y=b;
}
};
node atm[100005];
bool cmp(node a,node b)
{
return a.y<b.y;
}
int ans[100005];
int main()
{
int t;
cin>>t;
int cnt=1;
while(t--)
{
int k,i,j;
int x,y;
memset(vis,0,sizeof vis);
memset(tp.tree,0,sizeof tp.tree);
id=0;
for (i=1; i<=n; i++) mp[i].clear();
cin>>n>>k;
for (i=1; i<=n; i++)
scanf("%d",&aa[i]),dis[i]=aa[i];
sort(dis+1,dis+1+n);
int sz=unique(dis+1,dis+1+n)-dis-1;
for (i=1; i<=sz; i++) pos[i].clear(),pos[i].push_back(0); //初始化第0个位置
for (i=1; i<=n-1; i++)
{
scanf("%d%d",&x,&y);
mp[x].push_back(y);
mp[y].push_back(x);
}
dfs1(1); //求dfs序
int q;
cin>>q;
for ( i=1; i<=q; i++)
{
scanf("%d",&x);
atm[i].x=in[x]; //管辖区间范围
atm[i].y=out[x];
atm[i].id=i;
}
sort(atm+1,atm+1+n,cmp);
j=1;
for ( i=1; i<=n; i++)
{
x=ren[i ];
x=lower_bound(dis+1,dis+1+sz,aa[x])-dis; //离散化对应下标
pos[x].push_back(i);
y=pos[x].size()-1; //出现过y次
if (y>=k)
{
tp.add(pos[x][y-k]+1,1); //答案+1的区间
tp.add(pos[x][y-k+1]+1,-1);
if (y>k) //答案-1的区间
{
tp.add(pos[x][y-k-1]+1,-1);
tp.add(pos[x][y-k]+1,1);
}
}
for (;j<=q&&atm[j].y==i;j++) //处理i为右端点的询问
ans[atm[j].id]=tp.get(atm[j].x);//printf("%d",tp.get(atm[j].x));
}
printf("Case #%d:\n",cnt++);
for (i=1;i<=q;i++)
printf("%d\n",ans[i]);
if (t)printf("\n");
}
return 0;
}