Boring counting HDU - 4358(树上出现k次的数字个数)

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)/~

你可能感兴趣的:(树状数组)