dp的练习总结(8)

CF708C Centroids

1.如果节点u不是重心,那么它有且仅有一个size v ≥n/2(重儿子),则要在重儿子中找一个大小为s的树,满足2*(size v -s)≤n,2*s≤n

2.假设是一棵以重心为根的有根树,则对于节点u≠root,其重儿子大小为n-size u,即断掉u与其fa的连边后的另一棵树,那么问题转化为:对于每一个 uu 判断上边有无一棵大小满足不在 u 的子树内且 n/2−size u ≤S≤n/2 的树。

3.具体实现:维护两棵权值树状数组t1和t2

        t1[i]:维护全局的大小小于 i 的个数

        t2[i]:维护当前扫过点的子树中大小小于 i 的个数

对于一个u,其子树内满足条件的数量就是扫完u之后t2的变化量,需要将其减去

代码实现如下:

#include <bits/stdc++.h>
using namespace std;
const int N=400005;
int n,E,root,siz[N],ans[N],t1[N],t2[N];
int pnt[N*2],nxt[N*2],head[N];
void add_e(int u,int v) {
	pnt[++E]=v,nxt[E]=head[u],head[u]=E;
}
void prp(int u,int f) {
	siz[u]=1;
	bool ok=1;
	for(int i=head[u];i;i=nxt[i]) {
		int v = pnt[i];
		if(v == f) continue;
		prp(v,u);
		siz[u] += siz[v];
		if(siz[v] > n/2) ok=0;
	}
	if(n-siz[u] > n/2) ok=0;
	if(ok) root=u, ans[u]=1;
}
void add(int a[],int x,int v) {if(x<=0) return;for(++x;x<=n+1;x+=x&-x) a[x]+=v;}
int ask(int a[],int x) {
	if(x <= 0) return 0;
	int res = 0;
	for(++x;x;x-=x&-x) res += a[x];
	return res;
}
void solve(int u,int fa) {	
	int t=0;
	add(t1,siz[fa],-1);
	add(t1,n-siz[u],1);
	if(ans[u] == 0) {
		t += ask(t1,n/2);
		t -= ask(t1,(n-2*siz[u]+1)/2-1);
		t += ask(t2,n/2);
		t -= ask(t2,(n-2*siz[u]+1)/2-1);
	}
	add(t2,siz[u],1);
	for(int i=head[u];i;i=nxt[i]) {
		int v = pnt[i];
		if(v==fa) continue;
		solve(v,u);
	}
	add(t1,siz[fa],1);
	add(t1,n-siz[u],-1);
	if(ans[u] == 0) {
		t -= ask(t2,n/2);
		t += ask(t2,(n-2*siz[u]+1)/2-1);
		if(t > 0) ans[u]=1;
	}
}
int main(){
	scanf("%d",&n);
	for(int u,v,i=1;i<n;i++) {
		scanf("%d%d",&u,&v);
		add_e(u,v), add_e(v,u);
	}
	prp(1,0);
	prp(root,0);
	for(int i=0;i<=n;i++) add(t1,siz[i],1);
	solve(root,0);
	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
	return printf("\n"),0;
}

P3668 Modern Art 2 G

1.记录每一种颜色的起始点以及终点,可以想象,如果染色次数要最少,那么最外一层的颜色要最先涂

2.可以想象出一个栈,当前点的的起始位置加入栈,如果这一段有颜色不同于这个点的,就不可能,碰到当前点的终止位置,就出栈,最大染色次数就是栈最大的size,可以画个图试一下,当size 变化为 1 2 3 2 1 时,两边明显是可同时进行的,所以答案应该只要记录最大值

0是不填颜色,所以它的出栈位置记录为n+1,先让0入栈,并让其最后出栈,但是这并不会记录进染色次数,所以答案要-1

最后代码呈上:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n,a[maxn],book[maxn],ans,top;
int s[maxn],e[maxn],sta[maxn];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		if(!s[a[i]]) s[a[i]]=i;
		e[a[i]]=i;
	}
	e[0]=n+1;a[n+1]=0;
	for(int i=0,x;i<=n+1;++i){
		x=a[i];
		if(i==s[x]) sta[++top]=x,ans=max(ans,top);
		if(x!=sta[top]){
			printf("-1");
			return 0;
		}
		if(i==e[x]) top--;
	}
	printf("%d",ans-1);
	return 0;
}

  • 12
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值