P8844 [传智杯 #4 初赛] 小卡与落叶 题解

分析

乱搞题。

1 ≤ n , m ≤ 1 0 5 1 \le n,m \le 10^5 1n,m105 的时候就可以考虑乱搞了。

发现每次操作 1 1 1 都会把上一次的操作 1 1 1 覆盖掉,那么第 i i i 个询问时树的颜色情况就是由前 1 1 1 个操作 1 1 1 决定。也就是说这个询问的内容变成了:在 x x x 为根的子树中,深度不小于 x ′ x' x 的节点数量。 x ′ x' x 是该操作 1 1 1 x x x

求这玩意直接树上启发式合并。记录答案的时候用值域分块求一下和就行。复杂度 O ( m n ) O(m \sqrt{n}) O(mn )

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define re register
#define il inline
#define PII pair<int,int>
#define x first
#define y second

const int N=1e5+10;
int n,m;
int ne[N<<1],e[N<<1],h[N],idx;
int dep[N],mson[N],siz[N];
int in[N],out[N],dfsx[N],cnt;
int cnt_dep[N];
int sum[N],len;
vector<PII> Q[N];
int ans[N],qidx;

il void add(int a,int b){ne[++idx]=h[a],e[idx]=b,h[a]=idx;}
il int get(int x){return (x-1)/len+1;}
il void Add(int x){++cnt_dep[dep[x]],++sum[get(dep[x])];}
il void Del(int x){--cnt_dep[dep[x]],--sum[get(dep[x])];}
il void merge(int x,int flag){
	for(re int i=in[x];i<=out[x];++i)
		if(flag) Add(dfsx[i]);
		else Del(dfsx[i]);
	return ;
}
il int query(int x){
	int l=x,r=n,bl=get(x),br=get(n),ans=0;
	if(bl==br){
		for(re int i=l;i<=r;++i) ans+=cnt_dep[i];
		return ans;
	}
	for(re int i=l;i<=bl*len;++i) ans+=cnt_dep[i];
	for(re int bk=bl+1;bk<=br-1;++bk) ans+=sum[bk];
	for(re int i=(br-1)*len+1;i<=r;++i) ans+=cnt_dep[i];
	return ans;
}
il void dfs1(int now,int fa){
	dfsx[++cnt]=now,in[now]=cnt;
	dep[now]=dep[fa]+1,mson[now]=-1,siz[now]=1;
	int msiz=0;
	for(re int i=h[now];i;i=ne[i]){
		int j=e[i];if(j==fa) continue;
		dfs1(j,now),siz[now]+=siz[j];
		if(msiz<siz[j]) msiz=siz[j],mson[now]=j;	
	}
	out[now]=cnt;
	return ;
}
il void dfs2(int now,int fa,int flag){
	for(re int i=h[now];i;i=ne[i]){
		int j=e[i];if(j==fa||j==mson[now]) continue;
		dfs2(j,now,1);
	}
	if(mson[now]!=-1) dfs2(mson[now],now,0);
	for(re int i=h[now];i;i=ne[i]){
		int j=e[i];if(j==fa||j==mson[now]) continue;
		merge(j,1);
	}
	Add(now);
	for(re int j=0;j<Q[now].size();++j) ans[Q[now][j].x]=query(Q[now][j].y);
	if(flag) merge(now,0);
	return ;
}

il void solve(){
	cin>>n>>m;len=sqrt(n);
	for(re int i=1,a,b;i<n;++i) 
		cin>>a>>b,add(a,b),add(b,a);
	int min_y=n+1;
	for(re int i=1;i<=m;++i){
		int op,x;cin>>op>>x;
		if(op==1) min_y=x;
		else Q[x].push_back({++qidx,min_y});
	}
	dfs1(1,0),dfs2(1,0,0);
	for(re int i=1;i<=qidx;++i) cout<<ans[i]<<"\n";
}

signed main(){
	solve();
	return 0;
}
  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
第六届传智杯B组初赛是传智播客举办的一次IT技术竞赛的初赛阶段,旨在选拔出各高校优秀的程序设计人才。该比赛中,参赛者将面临多道编程题目,通过编写程序解决问题来展示他们的技术水平和创新能力。 在初赛中,参赛者需要在规定的时间内完成多道程序设计题目。这些题目可能涉及数据结构、算法、网络通信等方面的知识,要求参赛者具备扎实的编程基础和解决实际问题的能力。 参赛者需要在规定的时间内完成编程题目,并提交给评委进行评分。评委会根据答案的正确性、效率、代码的可读性等方面对参赛者的作品进行综合评判。最终,得分高的参赛者将进入下一轮比赛。 第六届传智杯B组初赛的目的是为了选拔出具备优秀编程能力的学生,为他们提供一个展示才华、学习交流的平台。参赛者不仅可以通过比赛锻炼自己的编程技巧,还可以结识其他优秀的参赛者,相互学习、切磋技艺。 在比赛过程中,参赛者还可以通过与其他选手交流,了解各种不同的编程思路和解题方法,不断提高自己的编程水平。同时,参赛者还有机会与业界的专家学者进行交流,了解最新的技术动态和发展趋势。 总之,第六届传智杯B组初赛是一次很有意义的编程竞赛,为各大高校的IT人才选拔提供了一次难得的机会。通过比赛,参赛者可以展现自己的才华,提升技术水平,同时也可以与其他优秀选手进行交流,共同进步。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

harmis_yz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值