【校内模拟】颜色对(均摊分析)

传送门


解析:

暴力很好想,我们将所有询问离线,用线性表标注在儿子的颜色上,然后跑 d f s dfs dfs,记录当前节点到根的所有颜色出现的次数,然后就可以统计了。

但是这样会被卡成 O ( n 2 ) O(n^2) O(n2)。。。试想如果一个点的颜色对应很多的询问,那么这些询问难道都需要被回答一次?当然你可以对询问优化,但是没什么用啊。。。要卡的话还是能够卡成 O ( n 2 ) O(n^2) O(n2)

思路:考虑分类讨论

真正卡住我们的是什么?

当一个颜色的点有很多的时候,所有关于这个颜色的询问都会被回答。

于是就咕咕咕了。

考虑当一个颜色的点多到一定程度,我们就单独处理一次所有与这个颜色有关的询问。

显然,如果只考虑当前颜色的话,我们是能够在 O ( n ) O(n) O(n)时间通过 d f s dfs dfs处理出每个点子树内部和到根的路径上当前颜色的节点的数量。

然后我们在 d f s dfs dfs的时候顺便统计,就可以做到 O ( n ) O(n) O(n)回答所有与颜色 c o l col col有关的询问。

我们设置这个界为 n \sqrt n n ,这样就会有不超过 O ( n ) O(\sqrt n) O(n )种颜色需要统计,这部分的复杂度就是 O ( n n ) O(n\sqrt n) O(nn )

剩下的所有颜色都只有不超过 O ( n ) O(\sqrt n) O(n )个点,所以可以直接来一遍 O ( n ) O(n) O(n)dfs的同时统计答案,这部分复杂度也是 O ( n n ) O(n\sqrt n) O(nn )


代码(严格 O ( n n ) O(n\sqrt n) O(nn )):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

cs int N=100005,B=200005;
int n,r,q;

vector<int> edge[N];
inline void addedge(int u,int v){
	edge[u].push_back(v);
}

int fa[N],siz[N],id[N],dfs_clock;
int tim[N];

void dfs(int u){
	id[++dfs_clock]=u;
	for(int re e=0;e<edge[u].size();++e)dfs(edge[u][e]);
}

int col[N],cnt[N];

int ans1[N],ans2[N];

inline void calc(int c){
	memset(ans1,0,sizeof ans1);
	memset(ans2,0,sizeof ans2);
	memset(siz,0,sizeof siz);
	for(int re i=1;i<=n;++i){
		int u=id[i];
		siz[u]=siz[fa[u]];
		ans1[col[u]]+=siz[u];
		if(col[u]==c)++siz[u];
	}
	memset(siz,0,sizeof siz);
	for(int re i=n;i;--i){
		int u=id[i];
		for(int re e=0;e<edge[u].size();++e)siz[u]+=siz[edge[u][e]];
		ans2[col[u]]+=siz[u];
		if(col[u]==c)++siz[u];
	}
}

int qa[B],qb[B],ans[B];
bool sol[B];

vector<pair<int,int> > qu[N];
inline void dfs_solve(int u){
	vector<pair<int,int> > &vec=qu[col[u]];
	for(int re i=0;i<vec.size();++i)ans[vec[i].second]+=cnt[vec[i].first];
	++cnt[col[u]];
	for(int re e=0;e<edge[u].size();++e)dfs_solve(edge[u][e]);
	--cnt[col[u]];
}

inline void solve(){
	for(int re i=1;i<=q;++i)if(!sol[i])qu[qb[i]].push_back(make_pair(qa[i],i));
	memset(cnt,0,sizeof cnt);
	dfs_solve(1);
}

signed main(){
	n=getint(),r=getint(),q=getint();
	++cnt[col[1]=getint()];
	for(int re i=2;i<=n;++i){
		addedge(fa[i]=getint(),i);
		++cnt[col[i]=getint()];
	}
	dfs(1);
	for(int re i=1;i<=q;++i)++tim[qa[i]=getint()],++tim[qb[i]=getint()];
	for(int re i=1,lim=sqrt(n);i<=r;++i){
		if(cnt[i]>=lim&&tim[i]){
			calc(i);
			for(int re j=1;j<=q;++j)
			if(!sol[j])if(qa[j]==i)sol[j]=true,ans[j]=ans1[qb[j]];
			else if(qb[j]==i)sol[j]=true,ans[j]=ans2[qa[j]];
		}
	}
	solve();
	for(int re i=1;i<=q;++i)cout<<ans[i]<<"\n";
	return 0;
}

代码(乱搞):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<18|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

cs int N=100005,Q=200005;
int n,r,q;

vector<pair<int,int> > qu[N];

ll ans[Q];
int f[Q];
inline int getfa(int x){
	while(x^f[x])x=f[x]=f[f[x]];
	return x;
}

int last[N],nxt[N],to[N],ecnt;
inline void addedge(int u,int v){
	nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v;
}

int col[N],b[N];

int cnt[N];
void dfs(cs int &u){
	vector<pair<int,int> > &vec=qu[col[u]];
	for(int re i=0;i<vec.size();++i)ans[vec[i].second]+=cnt[vec[i].first];
	++cnt[col[u]];
	for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]])
	dfs(v);
	--cnt[col[u]];
}

signed main(){
	n=getint();r=getint();q=getint();
	col[1]=b[1]=getint();
	for(int re i=2;i<=n;++i){
		addedge(getint(),i);
		col[i]=b[i]=getint();
	}
	sort(b+1,b+n+1);
	r=unique(b+1,b+n+1)-b-1;
	for(int re i=1;i<=n;++i)col[i]=lower_bound(b+1,b+r+1,col[i])-b;
	for(int re i=1;i<=q;++i){
		f[i]=i;
		int u=getint(),v=getint();
		int posu=lower_bound(b+1,b+r+1,u)-b,posv=lower_bound(b+1,b+r+1,v)-b;
		if(posu>r||posv>r||b[posu]!=u||b[posv]!=v)continue;
		qu[posv].push_back(make_pair(posu,i));
	}
	for(int re i=1;i<=r;++i){
		if(qu[i].size()<=1)continue;
		sort(qu[i].begin(),qu[i].end());
		vector<pair<int,int> > &vec=qu[i];
		int j=0,p=1;
		for(;p<vec.size();++p){
			if(vec[p].first!=vec[p-1].first)vec[++j]=vec[p];
			else f[vec[p].second]=vec[j].second;
		}
		vec.erase(vec.begin()+j+1,vec.end());
	}
	dfs(1);
	for(int re i=1;i<=q;++i)cout<<ans[getfa(i)]<<"\n";
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值