hdu4358 树状数组-非常经典

碰到过类似这题几次都做不出,只因为始终不理解

这题值得推荐的理由:离散化,树形转化为数组的线性结构,离线做法,树状数组,排序后边插入边询问等多种处理技巧

1.深搜后树上的某点的子树变成数组的一个区间,网上有人估计写挫了,说会爆栈,我不会,

最好不用vector,会TLE,这题数据挺强的,后来改了邻接表过了,事实证明,邻接表还是时间空间都有很好效果的做法

2.讲询问按右端点排序,每次插入更新,如果有这个是某个询问的右端点,就询问

3.在插入到某点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的答案)

可见要用到的操作:区间更新,单点询问,用第二类树状数组


~~1.离散化的时候搞错,一直RE不止

~~2vector用的过多导致TLE

~~3.最后还PE了。。。

果然历经艰辛啊


#include <iostream>
#include <map>
#include <algorithm>
#include <vector>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <stack>
using namespace std;
#define N 105000
int edge[N*2],head[N*2],adj[N*2],e;
int dfn[N],low[N],val[N],n,K,w[N],cnt,q;
vector<int>vec[N];
int vis[N];
struct Query
{
	int l,r,id;
}que[N];
bool cmp(Query left,Query right)
{
	return left.r==right.r?left.l<right.l:left.r<right.r;
}
int ans[N],r[N];int b[N];
bool cmp2(int left,int right)
{
	return w[left]==w[right]?left<right:w[left]<w[right];
}
void addedge(int u,int v)
{
	edge[e]=v;adj[e]=head[u];head[u]=e++;
}
void dfs(int u,int fa)
{
	dfn[u]=++cnt;val[cnt]=w[u];
	for(int i=head[u];i!=-1;i=adj[i])
		if(edge[i]!=fa)
			dfs(edge[i],u);
	low[u]=cnt;
}

int lowbit(int x)
{
	return x&(-x);
}
void update(int x,int v)
{
	while(x<=n)
	{
		b[x]+=v;
		x+=lowbit(x);
	}
}
int getsum(int x)
{
	int ret=0;
	while(x>0)
	{
		ret+=b[x];
		x-=lowbit(x);
	}
	return ret;
}

int main ()
{
	//freopen("input.txt","r",stdin);
	//freopen("output.txt","w",stdout);
	int test;scanf("%d",&test);
	for(int k=1;k<=test;++k)
	{
		scanf("%d%d",&n,&K);
		for(int i=0;i<=n;++i)
			head[i]=-1,b[i]=0;
		cnt=e=0;
		for(int i=1;i<=n;++i)
			scanf("%d",&w[i]),r[i]=i;
		sort(r+1,r+1+n,cmp2);
		int pre=w[r[1]]-1;
		for(int i=1;i<=n;++i)
			if(pre!=w[r[i]])
				pre=w[r[i]],w[r[i]]=++cnt;
			else w[r[i]]=cnt;

		int u,v;
		for(int i=1;i<n;++i)
		{
			scanf("%d%d",&u,&v);
			addedge(u,v);
			addedge(v,u);
		}
		cnt=0;
		dfs(1,-1);
		scanf("%d",&q);
		for(int i=1;i<=q;++i)
		{
			scanf("%d",&u);
			que[i].l=dfn[u];
			que[i].r=low[u];
			que[i].id=i;
		}
		sort(que+1,que+1+q,cmp);
		for(int i=0;i<=n;++i)
			vec[i].clear(),vec[i].push_back(0);
		int size,q_index=1;
		for(int i=1;i<=n;++i)
		{
			vec[val[i]].push_back(i);
				size=vec[val[i]].size()-1;
			if(size>=K)
			{
				if(size>K)
				{
					update(vec[val[i]][size-K-1]+1,-1);
					update(vec[val[i]][size-K]+1,1);
				}
				update(vec[val[i]][size-K]+1,1);
				update(vec[val[i]][size-K+1]+1,-1);
			}
			while(que[q_index].r==i&&q_index<=q)
			{
				ans[que[q_index].id]=getsum(que[q_index].l);
				q_index++;
			}
		}

		printf("Case #%d:\n",k);
		for(int i=1;i<=q;++i)
			printf("%d\n",ans[i]);
		if(k<test)
			printf("\n");
	}
	//system("pause");
	return 0;
}


你可能感兴趣的:(hdu4358 树状数组-非常经典)