c2020HXW,天下我有的 CSP 考前练习赛

树上的数

树上统计类的题目可以尝试往 \(dsu\) \(on\) \(tree\) 方向想。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define pb push_back
using namespace std;
const int N=3e5+3;
struct hh{
	int to,nxt;
}e[N<<1];
int n,num,fir[N],a[N],vis[N],pri[N],vv[N],b1[N],b2[N],cc[N],c[N];
int dep[N],siz[N],son[N];
LL sum[N],ans;
vector<int>v[N];
IL int in(){
	char c;int f=1;
	while((c=getchar())<'0'||c>'9')
	  if(c=='-') f=-1;
	int x=c-'0';
	while((c=getchar())>='0'&&c<='9')
	  x=x*10+c-'0';
	return x*f;
}
IL void ini(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
void init(){
	for(int i=1;i<N;++i) vv[i]=1,v[i].pb(1);
	for(int i=2;i<N;++i)
	  if(!b1[i]){
	  	v[i].pb(i);
	  	if(!b2[i]) vv[i]=i,cc[i]=1;
			for(int j=i+i;j<N;j+=i){
				if(!b2[i]) vv[j]*=i;
				cc[j]+=!b2[i],v[j].pb(i),b2[j]=1;
			}
			if(!b2[i]&&i<=2000) for(int j=i*i;j<N;j+=i*i) b1[j]=1;
		}
}
void dfs1(int u,int fa){
	siz[u]=1,dep[u]=dep[fa]+1;
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
	  if(v^fa){
	  	dfs1(v,u),siz[u]+=siz[v];
	  	if(siz[son[u]]<siz[v]) son[u]=v;
		}
}
IL void add(int u,int op){for(int i=0;i<v[a[u]].size();++i) sum[v[a[u]][i]]+=op*dep[u],c[v[a[u]][i]]+=op;}
IL void Add(int u,int fa,int op){
	add(u,op);
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
	  if(v^fa) Add(v,u,op);
}
void calc(int u,int fa,int x){
	for(int i=0;i<v[a[u]].size();++i) 
	  ans+=(cc[v[a[u]][i]]&1?-1:1)*(sum[v[a[u]][i]]+1ll*(dep[u]-2*dep[x])*c[v[a[u]][i]]);
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
	  if(v^fa) calc(v,u,x);
}
void dfs2(int u,int fa,int op){
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
	  if(v^fa&&v^son[u]) dfs2(v,u,1);
	if(son[u]) dfs2(son[u],u,0);
	for(int i=0;i<v[a[u]].size();++i)
	  ans+=(cc[v[a[u]][i]]&1?-1:1)*(sum[v[a[u]][i]]-1ll*c[v[a[u]][i]]*dep[u]);
	add(u,1);
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
	  if(v^fa&&v^son[u]) calc(v,u,u),Add(v,u,1);
	if(op) Add(u,fa,-1);
}
int main()
{
	int x,y;
	init(),n=in();
	for(int i=1;i<=n;++i) a[i]=vv[in()];
	for(int i=1;i<n;++i)
	  x=in(),y=in(),
	  ini(x,y),ini(y,x);
	dfs1(1,0),dfs2(1,0,0);
	printf("%lld\n",ans);
	return 0;
}

树上的树

子树差分,树剖做法有点类似于动态dp。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define pb push_back
using namespace std;
const int N=1e6+3;
struct hh{
	int to,nxt;
}e[N<<1];
struct kk{
	int to,id;
};
int n,m,fir[N],num,fa[N],st[N],ed[N],vis[N];
int X[N],Y[N],w[N];
LL f[N];int  val[N];
vector<int>pi[N];
vector<kk>p[N];
struct BIT{
	LL c[N];
	IL int lb(int x){return x&-x;}
	IL void add(int y,LL x){for(;y<=n;y+=lb(y)) c[y]+=x;}
	IL LL ask(int y){LL res=0;for(;y;y-=lb(y)) res+=c[y];return res;}
}T;
IL int in(){
	char c;int f=1;
	while((c=getchar())<'0'||c>'9')
	  if(c=='-') f=-1;
	int x=c-'0';
	while((c=getchar())>='0'&&c<='9')
	  x=x*10+c-'0';
	return x*f;
}
IL LL max(LL x,LL y){return x>y?x:y;}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void dfs1(int u,int fa){
	st[u]=++num;
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
	  if(v^fa) dfs1(v,u);
	ed[u]=num;
}
void dfs2(int u){
	LL Sum=0,res=0;vis[u]=1;
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
	  if(!vis[v]) dfs2(v),Sum+=f[v],fa[v]=u;
	for(int i=0,v;i<p[u].size();++i)
	  if(vis[v=p[u][i].to]==2) pi[find(v)].pb(p[u][i].id);
	T.add(st[u],Sum);
	for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
	  if(vis[v]==2) T.add(st[v],res-f[v]),res=f[v];
	T.add(ed[u]+1,res-Sum);
	for(int i=0,v;i<pi[u].size();++i){
		v=pi[u][i];
		f[u]=max(f[u],T.ask(st[X[v]])+T.ask(st[Y[v]])+w[v]-Sum);
	}
	f[u]=max(f[u],Sum+val[u]);
	vis[u]=2;
}
signed main()
{
	int x,y,z;
	n=in(),m=in();
	for(int i=1;i<=n;++i) fa[i]=i;
	for(int i=1;i<n;++i)
	  x=in(),y=in(),
	  add(x,y),add(y,x);
	num=0,dfs1(1,0);
	for(int i=1;i<=m;++i){
		X[i]=in(),Y[i]=in(),w[i]=in();
		if(X[i]==Y[i]) val[X[i]]=max(val[X[i]],w[i]);
		else p[X[i]].pb((kk){Y[i],i}),p[Y[i]].pb((kk){X[i],i});
	}
	dfs2(1);
	printf("%lld\n",f[1]);
	return 0;
}
posted @ 2020-11-02 14:14  (o-ωq)).oO  阅读(104)  评论(0编辑  收藏  举报