In this problem we consider a rooted tree with N vertices. The vertices are numbered from 1 to N, and vertex 1 represents the root. There are integer weights on each vectice. Your task is to answer a list of queries, for each query, please tell us among all the vertices in the subtree rooted at vertice u, how many different kinds of weights appear exactly K times?
Input
The first line of the input contains an integer T( T<= 5 ), indicating the number of test cases.
For each test case, the first line contains two integers N and K, as described above. ( 1<= N <= 10 5, 1 <= K <= N )
Then come N integers in the second line, they are the weights of vertice 1 to N. ( 0 <= weight <= 10 9 )
For next N-1 lines, each line contains two vertices u and v, which is connected in the tree.
Next line is a integer Q, representing the number of queries. (1 <= Q <= 10 5)
For next Q lines, each with an integer u, as the root of the subtree described above.
Output
For each test case, output “Case #X:” first, X is the test number. Then output Q lines, each with a number – the answer to each query.
Seperate each test case with an empty line.
Sample Input
1
3 1
1 2 2
1 2
1 3
3
2
1
3
Sample Output
Case #1:
1
1
1
这个题和eduoj3307的题目一样的,但是这个题需要树形转换成线性的。
离散化,树形转化为数组的线性结构,离线做法,树状数组,排序后边插入边询问等多种处理技巧。
因为这个题目数据量比较大,需要离散化一下。
.在插入到某点i是,第k(1<=k<=i)表示k到i的答案,要明白:
每插入新的一点,改变的只有插入的这个数,设这个数P出现的位置是P0,P1,P2…Pj-k-1,Pj-k,Pj-k+1…Pj-1,
如果询问的左端点在(Pj-k-1,Pj-k],右端点在i,P就凑够K次
现在在Pj=i插入新的数P,就变成如果询问的左端点在(Pj-k,Pj-k+1],P凑够K次,
而左端点在(Pj-k-1,Pj-k]的P出现的次数变成K+1,不是答案,
所以树状数组的操作是(Pj-k-1,Pj-k] 区间-1,(Pj-k,Pj-k+1]区间+1,然后询问左端点的值就好了(因为树状数组上第k位表示k到i的答案)
可见要用到的操作:区间更新,单点询问,用第二类树状数组
代码如下:
#include
#define ll long long
using namespace std;
const int maxx=1e5+100;
struct node{
int l;
int r;
int id;
bool operator<(const node &a)const{
return a.r>r;
}
}b[maxx];
struct Node{
int to,next;
}e[maxx<<1];
vector<int> v[maxx];
int head[maxx<<1],in[maxx],out[maxx],a[maxx],pre[maxx];ll c[maxx],ans[maxx];
int n,m,k,tot,sign;
/*-------------事前准备--------------*/
inline void init()
{
memset(head,-1,sizeof(head));
memset(c,0,sizeof(c));
tot=sign=0;
}
inline void add(int u,int v)
{
e[tot].to=v,e[tot].next=head[u],head[u]=tot++;
}
/*---------------dfs---------------*/
inline void dfs(int u,int f)
{
in[u]=++sign;
pre[sign]=a[u];
for(int i=head[u];i!=-1;i=e[i].next)
{
int to=e[i].to;
if(to==f) continue;
dfs(to,u);
}
out[u]=sign;
}
/*------------树状数组------------*/
inline int lowbit(int x){return x&-x;}
inline void update(int cur,int v){while(cur<=maxx) c[cur]+=v,cur+=lowbit(cur);}
inline ll query(int cur){int sum=0;while(cur>0) sum+=c[cur],cur-=lowbit(cur);return sum;}
int main()
{
int t,kk=0,x,y;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
init();map<int,int> mp;
int cnt=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(mp[a[i]]==0)
{
mp[a[i]]=++cnt;
a[i]=cnt;
v[cnt].clear();
v[cnt].push_back(0);
}
else a[i]=mp[a[i]];
}
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
dfs(1,0);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d",&x);
b[i].l=in[x];b[i].r=out[x];b[i].id=i;
}
sort(b+1,b+1+m);
for(int i=1,j=1;j<=m&&i<=n;i++)
{
x=pre[i];
v[x].push_back(i);
y=v[x].size()-1;
if(y>=k)
{
if(y>k)//出现次数大于k次了,就需要把之前的标记取消掉,类似于统计区间数字的操作。
{
update(v[x][y-k-1]+1,-1);
update(v[x][y-k]+1,1);
}
update(v[x][y-k]+1,1);//增加新的标记
update(v[x][y-k+1]+1,-1);
}
while(b[j].r==i&&j<=m) ans[b[j].id]=query(b[j++].l);
}
printf("Case #%d:\n",++kk);
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
if(t) puts("");
}
}
eduoj的题目链接:eduoj3307
这是统计区间出现次数为两次的数字个数
代码如下:
#include
#define ll long long
using namespace std;
const int maxx=5e5+100;
struct Node{
int l;
int r;
int id;
bool operator <(const Node &a)const{
return a.r>r;
}
}e[maxx];
int a[maxx],c[maxx],ans[maxx];
vector<int> v[maxx];
int n,m;
/*-------------树状数组-------------*/
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int val)
{
for(int i=x;i<=n;i+=lowbit(i))
c[i]+=val;
}
int query(int x)
{
int ans=0;
for(int i=x;i>0;i-=lowbit(i))
ans+=c[i];
return ans;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
int cnt=0,len;
map<int,int> mp;
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(!mp[a[i]])
{
mp[a[i]]=++cnt;
a[i]=cnt;
v[cnt].clear();
v[cnt].push_back(0);
}
else a[i]=mp[a[i]];
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&e[i].l,&e[i].r);
e[i].id=i;
}
sort(e+1,e+1+m);
for(int i=1,pos=1;i<=n;i++)
{
int u=a[i];
v[u].push_back(i);
int len=v[u].size()-1;
if(len>=2)
{
if(len>2)//消除上一次含K个U值的区间更新
{
update(v[u][len-3]+1,-1);
update(v[u][len-2]+1,1);
}
update(v[u][len-2]+1,1);
update(v[u][len-1]+1,-1);
}
while(e[pos].r==i)
{
ans[e[pos].id]=query(e[pos].l);
pos++;
}
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}
return 0;
}
努力加油a啊,(o)/~