启发式合并
算法理解
对于:
- 一开始有很多集合
- 只支持合并操作
- 每个集合有属性,合并后重新计算属性
的问题,我们可以进行启发式合并
启发式合并复杂度证明:
证明启发式合并,即证明每个点被访问次数不超过 \(log\) 次
T1:
先把总答案算出来
我们对于每一种颜色,维护一条链,合并时,将两条链收尾相接,接的时候,判断短的链的各个点是否与另一条链上的点相邻(即判断短的链上的相邻位置是否有和长链相同颜色的点)然后减去答案即可,
没调过,60分,恭喜我学会了对拍并且也没有调过
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,M=1e6+5;
int n,m,ans;
int en[M],len[M],b[M],a[N],p[N];
void merge(int x,int y){
if(x==y||len[x]==0||b[x]==b[y]) return;
if(len[b[x]]>len[b[y]]){
swap(b[x],b[y]);
}
int s1=b[x],s2=b[y];
for(int i=en[s1];i;i=p[i]){
// printf("%d ",i);
if(s2==a[i-1]) ans--;
if(s2==a[i+1]) ans--;
}
// printf("\n");
int pri=0;
for(int i=en[s1];i;i=p[i]){
a[i]=s2;
pri=i;
}
if(pri) p[pri]=en[s2],en[s2]=en[s1];
// printf("pri=%d ens2=%d ens1=%d s1=%d s2=%d\n",pri,en[s2],en[s1],s1,s2);
b[s1]=s2;
len[s2]+=len[s1];
en[x]=len[x]=0;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=N;i++) b[i]=i;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]!=a[i-1]) ans++;
p[i]=en[a[i]];en[a[i]]=i;
len[a[i]]++;
}
for(int i=1;i<=m;i++){
int op,x,y;
scanf("%d",&op);
if(op==2) printf("%d\n",ans);
else{
scanf("%d%d",&x,&y);
merge(x,y);
// printf("%d\n",ans);
// for(int j=1;j<=9;j++){
// printf("%d ",b[j]);
// }
// printf("\n");
}
}
}
T2:
考虑用 \(cnt[c[i]]\) 来处理每种颜色的数量,显然两个不交的集合合并是没有结合律的(只能暴力),并且也不能记忆化搜索(因为是数组)
所以只能考虑优化枚举顺序(类似于莫队思想)
考虑对于一个节点 \(u\) 它的所有孩子 \(v1,v2,...\) 的 \(cnt\) 可以合并成它的 \(cnt\)(但刚刚说了 \(cnt\) 不能合并),但是我们显然可以保留它的一个孩子的 \(cnt\) 暴力遍历其它孩子的答案,遂选择最大的孩子保留(树剖思想,相当于保留重儿子)
学过树剖的都知道,树剖的复杂度保证即为每一个点到根的轻边数小于 \(log\) 条(证明为每跳一条轻边节点数翻倍)
启发式合并复杂度证明:
假设统计 \(u\) 的答案,即为它的重儿子不需要访问(走过一条重边),轻儿子子树内所有点访问次数+1(走过一条轻边)
刚刚证明每一个点到根的轻边数小于 \(log\) 条,所以每个点被访问次数不超过 \(log\) 次
trick+1: 对于cnt这种集合运算的删除贡献可以再跑一边原函数,只不过贡献为-1
点击查看代码
#include<bits/stdc++.h>
#define fa(x) dot[x].fa
#define son(x) dot[x].son
#define siz(x) dot[x].siz
#define c(x) dot[x].c
using namespace std;
const int N=1e5+5;
struct node{
int son,c,fa,siz;
}dot[N];
int n,sum,mxnum,Son;
int cnt[N],ans[N];
vector<int>b[N];
void dfs1(int x,int f){
fa(x)=f;
int mson=0,nson=0;
for(int v:b[x]){
if(v==f) continue;
dfs1(v,x);
if(siz(v)>nson){
nson=siz(v);
mson=v;
}
siz(x)+=siz(v);
}
siz(x)++;
son(x)=mson;
}
void add(int x,int val){
cnt[c(x)]+=val;
if(cnt[c(x)]>mxnum){
mxnum=cnt[c(x)];
sum=c(x);
}
else if(cnt[c(x)]==mxnum){
sum+=c(x);
}
for(int v:b[x]){
if(v==fa(x)||v==Son) continue;
add(v,val);
}
}
void dfs2(int x,int op){
for(int v:b[x]){
if(v==son(x)||v==fa(x)) continue;
dfs2(v,0);
}
if(son(x)) dfs2(son(x),1);
Son=son(x);
add(x,1);
Son=0;
ans[x]=sum;
if(!op) add(x,-1),sum=0,mxnum=0;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&c(i));
}
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
b[u].push_back(v);
b[v].push_back(u);
}
dfs1(1,0);
dfs2(1,0);
for(int i=1;i<=n;i++){
printf("%d ",ans[i]);
}
}

浙公网安备 33010602011771号