nlog^2的平衡树启发式合并
我写挂了QAQ
T了
留坑待补。。
#include<cstdio>
#include<cstring>
#include<cstdlib>
const int N=1e5+7;
int a[N];
struct edgt
{
int color,l,r,w,rnd;
}tr[N];
int cnt;
int stack[N+100],top;
inline void rturn(int &k)
{
int t=tr[k].l;tr[k].l=tr[t].r;tr[t].r=k;
k=t;
}
inline int read()
{
int ans=0;char t=getchar();
while(t<'0'||t>'9') t=getchar();
while(t>='0'&&t<='9') ans=ans*10+t-'0',t=getchar();
return ans;
}
inline void lturn(int &k)
{
int t=tr[k].r;tr[k].r=tr[t].l;tr[t].l=k;
k=t;
}
#define lson tr[k].l
#define rson tr[k].r
void insert(int &k,int num,int tot)
{
if(!k){
k=stack[top--];tr[k].color=num;tr[k].rnd=rand();tr[k].w=tot;
return;
}
if(tr[k].color==num) tr[k].w+=tot;
else if(num<tr[k].color){
insert(tr[k].l,num,tot);if(tr[lson].rnd<tr[k].rnd) rturn(k);
}
else{
insert(tr[k].r,num,tot);if(tr[rson].rnd<tr[k].rnd) lturn(k);
}
}
void dfstree(int &ro,int k)
{
if(!k) return;
insert(ro,tr[k].color,tr[k].w);
dfstree(ro,lson);
dfstree(ro,rson);
stack[++top]=k;
tr[k].l=0,tr[k].r=0;
}
int maxans;
long long sum;
void dfs2(int k)
{
if(!k) return ;
if(tr[k].w>maxans) maxans=tr[k].w,sum=tr[k].color;
else if(tr[k].w==maxans) sum+=tr[k].color;
dfs2(lson);dfs2(rson);
}
struct node
{
int to,next;
}e[N*2];
int first[N];
inline void insert_edgt(int u,int v)
{
e[++cnt]=(node){v,first[u]};first[u]=cnt;
e[++cnt]=(node){u,first[v]};first[v]=cnt;
}
int size[N];
long long ans[N];
int rt[N];
//void test(int k)
//{
// if(!k) return;
// printf("%d %d\n",tr[k].color,tr[k].w);
// test(lson);
// test(rson);
//}
void dfs(int x,int fa)
{
size[x]=1;
int max=0,pos=-1;
for(int k=first[x];k;k=e[k].next)
if(e[k].to!=fa)
{
dfs(e[k].to,x);
size[x]+=size[ e[k].to ];
if(size[e[k].to]>max) max=size[e[k].to],pos=e[k].to;
}
if(pos!=-1) rt[x]=rt[pos];
insert(rt[x],a[x],1);
// if(x==1)printf("std::\n"),test(rt[1]);;
for(int k=first[x];k;k=e[k].next)
if(e[k].to!=fa&&e[k].to!=pos)
{
// if(e[k].to==6&&x==1) printf("aaaaaa %d %d\n",stack[top],top),test(rt[e[k].to]);
dfstree(rt[x],rt[e[k].to]);
rt[e[k].to]=0;
// if(x==1)printf("std::%d\n",e[k].to),test(rt[x]);;
}
maxans=0,sum=0;
dfs2(rt[x]);
ans[x]=sum;
}
int main()
{
srand(232);
int n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n+7;i++) stack[i]=i;
top=n+7;
for(int i=1;i<n;i++){
int u,v;u=read(),v=read(),insert_edgt(u,v);
}
dfs(1,-1);
for(int i=1;i<=n;i++) printf("%I64d ",ans[i]);
return 0;
}
dsu on tree:
其实就是个大暴力!
但由于轻重链剖分启发式合并的运用,所以复杂度是nlogn的
我们先预处理出每个点的重儿子
然后开始dfs
先递归下去处理轻儿子(在轻儿子的dfs过程中会删除该子树的贡献)
再处理重儿子(在重儿子的dfs过程中不会删除该子树的贡献)
最后再重新dfs(另一个函数update)一遍把轻儿子的贡献加起来(注意此时不要加重儿子的,因为已经保留了)
记录该节点的ans
然后如果该点是它的父亲的轻儿子,那么我们再dfs一遍把贡献删去(update)
实测78 ms
#include<cstdio>
#include<cstring>
inline int read()
{
int ans=0;char t=getchar();
while(t<'0'||t>'9') t=getchar();
while(t>='0'&&t<='9') ans=ans*10+t-'0',t=getchar();
return ans;
}
const int N=1e5+7;
int a[N];
struct node
{
int to,next;
}e[N*2];
int cnt,first[N];
inline void insert(int u,int v)
{
e[++cnt]=(node){v,first[u]};first[u]=cnt;
e[++cnt]=(node){u,first[v]};first[v]=cnt;
}
int size[N],heavy[N],tar[N];
void count(int x,int fa)
{
size[x]=1;int max=0,pos=-1;
for(int k=first[x];k;k=e[k].next)
if(e[k].to!=fa)
{
count(e[k].to,x),size[x]+=size[e[k].to];
if(size[e[k].to]>max) max=size[e[k].to],pos=e[k].to;
}
heavy[x]=pos;
if(pos!=-1)tar[pos]=1;
}
long long ans[N];
int color[N],max;
long long sum;
void update(int x,int fa,int num)
{
color[a[x]]+=num;
if(num>0)
{
if(color[a[x]]>max) max=color[a[x]],sum=a[x];
else if(color[a[x]]==max) sum+=a[x];
}
for(int k=first[x];k;k=e[k].next)
if(e[k].to!=fa&&e[k].to!=heavy[x]) update(e[k].to,x,num);
}
void dfs(int x,int fa)
{
for(int k=first[x];k;k=e[k].next)
if(e[k].to!=fa&&e[k].to!=heavy[x])
dfs(e[k].to,x);
if(heavy[x]!=-1) dfs(heavy[x],x);
update(x,fa,1);
heavy[x]=0;
ans[x]=sum;
if(!tar[x]) update(x,fa,-1),max=0;
}
int main()
{
int n=read(),u,v;
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<n;i++) u=read(),v=read(),insert(u,v);
count(1,-1);
// for(int i=1;i<=n;i++) printf("aa%d %d\n",heavy[i],tar[i]);
dfs(1,-1);
for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
return 0;
}
由于子树的dfs序是连续的
因此我们可以在dfs序上用莫队处理区间询问
如果要支持删除操作的话就得加数据结构,并且复杂度带一个log
所以我们考虑不带删除的莫队
对于左右端点在一个块的,我们直接for求出答案,单个询问复杂度sqrt(n)
对于左端点在块内,右端点在另一块的
因为右端点是递增的,我们直接和原来一样递增扫过去
同时左端点处从块的最右边暴力扫过去
然后再消除影响
左端点由于移动范围最多sqrt(n),n个询问
而右端点每个块最多O(n),sqrt(n)个块
所以总的复杂度还是nsqrt(n)的
实测124 ms
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
inline int read()
{
int ans=0;char t=getchar();
while(t<'0'||t>'9') t=getchar();
while(t>='0'&&t<='9') ans=ans*10+t-'0',t=getchar();
return ans;
}
const int N=1e5+7;
int a[N];
struct node
{
int to,next;
}e[N*2];
struct edgt
{
int l,r,order;
}f[N];
int pos[N];
bool cmp(edgt a,edgt b)
{
if(pos[a.l]==pos[b.l]) return a.r<b.r;
return pos[a.l]<pos[b.l];
}
int cnt,first[N];
inline void insert(int u,int v)
{
e[++cnt]=(node){v,first[u]};first[u]=cnt;
e[++cnt]=(node){u,first[v]};first[v]=cnt;
}
int qu[N];
void dfs(int x,int fa)
{
qu[++cnt]=x;f[x].l=cnt;f[x].order=x;
for(int k=first[x];k;k=e[k].next)
if(e[k].to!=fa) dfs(e[k].to,x);
f[x].r=cnt;
}
int n;
long long ans[N];
int sum[N],color[N],block_right[N];
void work()
{
int max=0,premax=0;
long long sum=0,presum=0;
int r=block_right[1];
for(int i=1,now=1;i<=n;i++)
{
if(f[i].l>block_right[now])
{
memset(color,0,sizeof(color));
max=0;sum=0;now++;
while(pos[f[i].l]>block_right[now]) now++;
r=block_right[now];
}
// printf("kkk::%d %d\n",i,now);
if(f[i].r<=block_right[now])
{
// max=0,sum=0;
// printf("aaaa%d\n",i);
for(int kk=f[i].l;kk<=f[i].r;kk++)
{
color[ a [ qu[kk] ] ]++;
if(color[ a [ qu[kk] ] ]>max) max=color[ a[ qu[kk] ] ],sum=a[ qu[kk] ];
else if(color[ a [ qu[kk] ] ]==max) sum+=a[ qu[kk] ];
}
for(int kk=f[i].l;kk<=f[i].r;kk++) color[ a [ qu[kk] ] ]--;
ans[f[i].order]=sum;
max=0;sum=0;
}
else
{
while(r<f[i].r)
{
r++;color[ a [ qu[r] ] ]++;
if(color[ a [ qu[r] ] ]>max) max=color[ a[ qu[r] ] ],sum=a[ qu[r] ];
else if(color[ a [ qu[r] ] ]==max) sum+=a[ qu[r] ];
}
premax=max,presum=sum;
int l=block_right[now]+1;
while(l>f[i].l)
{
l--;color[ a[ qu[l] ] ]++;
if(color[ a[ qu[l] ] ]>max) max=color[ a[ qu[l] ] ],sum=a[ qu[l] ];
else if(color[ a[ qu[l] ] ]==max) sum+=a[ qu[l] ];
}
ans[f[i].order]=sum;
max=premax,sum=presum;
while(l<block_right[now]+1) color[ a [ qu[l] ] ]--,l++;
}
}
}
int main()
{
int u,v;
n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<n;i++) u=read(),v=read(),insert(u,v);
cnt=0;
dfs(1,-1);
int block=sqrt(n);
for(int i=1;i<=n;i++) pos[i]=(i-1)/block+1;
for(int i=1;i<=n;i++)
{
block_right[i]=i*block;
if(block_right[i]>=n) {
block_right[i]=n;break;
}
}
// for(int i=1;i<=n;i++) printf("%d ",qu[i]);
// for(int i=1;i<=n;i++) printf("std::%d %d %d\n",f[i].l,f[i].r,f[i].order);
// printf("\n\n");
std::sort(f+1,f+1+n,cmp);
// for(int i=1;i<=n;i++) printf("std::%d %d\n",f[i].l,f[i].r);
work();
for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
return 0;
}