您的位置:首页 > 产品设计 > UI/UE

codeforces 221 div1 D Tree and Queries

2014-02-27 17:18 381 查看
题目大意:给一棵树,1为根,每个节点有一个权值。多次询问,每次询问为一个v和k。把v为根的这棵树中,所有节点的权加入一个集合。求,这个集合中,有几种数字出现的次数大于等于k。

最开始,老是去想可持续化数据结构,但是,我不会可持续化的平衡树啊 -_-|||

然后去找解题,有种看起来很叼的暴力算法,但是我看不懂啊。。。

最后,一位大神告诉我这是莫队算法。这里十分感谢这位大神。

我们还是使用欧拉序列把树形结构变成线性结构,问题变成了区间查询问题。

莫队算法算的上是对区间询问问题的一种离线的方法。

我们以无修改值的区间求和为例。

首先说一种最暴力的办法,即将区间每个点都加起来。当然,这是要超时的。

好吧,我们改进一下,假设有两次查询(第一次记为[l1,r1] ,第二次记为[l2,r2])。第一次还是暴力,第二次建立在第一次的结果上,当移动左右边界即可,复杂度为O(|l1-l2|+lr1-r2l)。如果有多次,还是按移动左右边界的方法。

这方法也不靠谱啊,感觉要超时。读者先不要着急。

我们计算前,先对多次查询排个序。假设数组有n个元素,记 int S=sqrt(n),询问的区间为[l,r],排序的的第一关键值为 l/S,第二关键值为 r。

排好序后,再按上面那种做法写,综合复杂度即为n*sqrt(n)。

下面说说为什么是n*sqrt(n)。

按第一关键值排序,等于把查询分成了sqrt(n)块。

对于每一块,每次左边界的移动次数是不会超过sqrt(n)的。在每一块中,按r从小到大排序(即r不降),那么,对于这一块,右边界的均摊复杂度最坏为O(n)。

而从一块移动到另一块,左右边界的移动次数也是不超过n次。

总共有sqrt(n)块,综合复杂度不会超过n*sqrt(n)。

和大神比起来,代码好丑啊。。。

//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef __int64 LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const int INF=1000111222;
const double INFF=1e200;
const double eps=1e-8;
const int mod=1000000007;
const int NN=100010;
const int MM=10010;
/* ****************** */

int a[NN],b[NN];
struct G
{
int v,next;
}E[NN*2];
int p[NN],T,tsp;
int ll[NN],rr[NN];
struct Q
{
int l,r,id,fg,k;
}line[NN];
int ans[NN],num[NN],ff[NN];

void add(int u,int v)
{
E[T].v=v;
E[T].next=p[u];
p[u]=T++;
}

void dfs(int u,int fa)
{
ll[u]=++tsp;
a[tsp]=b[u];
int i,v;
for(i=p[u];i+1;i=E[i].next)
{
v=E[i].v;
if(fa==v)
continue;
dfs(v,u);
}
rr[u]=tsp;
}

bool cmp(Q x,Q y)
{
if(x.fg==y.fg)
return x.r<y.r;
return x.fg<y.fg;
}

void solve(int n,int m)
{
sort(line+1,line+1+m,cmp);
int l,r,i;
l=1;
r=0;
for(i=1;i<=m;i++)
{
while(l>line[i].l)
{
l--;
num[a[l]]++;
ff[ num[a[l]] ]++;
}
while(l<line[i].l)
{
ff[ num[a[l]] ]--;
num[a[l]]--;
l++;
}
while(r>line[i].r)
{
ff[ num[a[r]] ]--;
num[a[r]]--;
r--;
}
while(r<line[i].r)
{
r++;
num[a[r]]++;
ff[ num[a[r]] ]++;
}
ans[ line[i].id ]=ff[ line[i].k ];
}
for(i=1;i<=m;i++)
{
printf("%d\n",ans[i]);
}
}

int main()
{
memset(p,-1,sizeof(p));
T=tsp=0;

int n,m,i,u,v,S;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",&b[i]);
for(i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}

dfs(1,-1);
S=sqrt(n+0.0);

for(i=1;i<=m;i++)
{
scanf("%d%d",&v,&line[i].k);
line[i].id=i;
line[i].l=ll[v];
line[i].r=rr[v];
line[i].fg=ll[v]/S;
}

solve(n,m);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: