【十二省联考2019】【LOJ3052】【洛谷P5290】【BZOJ5499】春节十二响(贪心)(树链剖分)(线段树)

41 篇文章 0 订阅
25 篇文章 0 订阅

LOJ传送门

洛谷传送门

BZOJ传送门


解析;

来给大家看一个 O ( n log ⁡ 2 n ) O(n\log ^2n) O(nlog2n)的菜鸡做法。

考场上 y y yy yy出来的。

来看一个正确性显然的贪心:

首先我们在当前没有选择的点中选出代价最大的点,将它的掌控范围,即它到根的链和它的子树,全部ban掉,即本轮禁选。将它的权值加入答案。

然后再在没有ban掉的点中,找到权值最大的点,ban掉掌控范围,但是权值不用加入答案了。

直到没有点可以选择,退出本轮,将所有ban掉的点恢复,并将已经选择了的点的权值设置为 0 0 0,如果所有点全部已经选择,退出,不然进入下一轮。

显然可以直接上树剖和线段树维护这个过程。


代码:

#include<bits/stdc++.h>
#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;

using std::cout;
using std::cerr;

typedef std::pair<int,int> pii;
#define mp std::make_pair
#define fi first
#define se second

cs int N=2e5+5;

ll ans;

int n,val[N];

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 fa[N],siz[N],son[N],top[N],dep[N];
int in[N],out[N],pos[N],dfs_clock;

void dfs1(int u){
	siz[u]=1;
	for(re int e=last[u],v=to[e];e;v=to[e=nxt[e]]){
		dep[v]=dep[u]+1;
		dfs1(v);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}

void dfs2(int u){
	pos[in[u]=++dfs_clock]=u;
	if(son[u]){
		top[son[u]]=top[u];
		dfs2(son[u]);
	}
	for(re int e=last[u],v=to[e];e;v=to[e=nxt[e]])if(v!=son[u]){
		top[v]=v;
		dfs2(v);
	}
	out[u]=dfs_clock;
}

pii mx[N<<2];
bool ban[N<<2],need[N<<2];
bool used[N];

inline void pushup(int k){
	mx[k]=std::max(mx[k<<1],mx[k<<1|1]);
	need[k]=ban[k]||need[k<<1]||need[k<<1|1];
}

inline void build(int k,int l,int r){
	if(l==r){
		mx[k]=mp(val[pos[l]],l);
		return ;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(k);
}

inline void modify_ban(int k,int l,int r,cs int &ql,cs int &qr){
	if(ban[k])return ;
	if(ql<=l&&r<=qr){
		mx[k]=mp(0,0);
		ban[k]=need[k]=true;
		return ;
	}
	int mid=(l+r)>>1;
	if(ql<=mid)modify_ban(k<<1,l,mid,ql,qr);
	if(mid<qr)modify_ban(k<<1|1,mid+1,r,ql,qr);
	pushup(k);
}

inline void recover(int k,int l,int r){
	if(!need[k])return ;
	if(ban[k])ban[k]=need[k]=false;
	if(l==r){mx[k]=used[pos[l]]?mp(0,l):mp(val[pos[l]],l);return ;}
	int mid=(l+r)>>1;
	recover(k<<1,l,mid);
	recover(k<<1|1,mid+1,r);
	pushup(k);
}

inline void modify(int k,int l,int r,cs int &pos,cs int &val){
	if(l==r){mx[k].fi=0;return ;}
	int mid=(l+r)>>1;
	if(pos<=mid)modify(k<<1,l,mid,pos,val);
	else modify(k<<1|1,mid+1,r,pos,val);
	pushup(k);
}

inline void find_conquer(int u){
	used[u]=true;
	modify_ban(1,1,n,in[u],out[u]);
	u=fa[u];
	while(u){
		modify_ban(1,1,n,in[top[u]],in[u]);
		u=fa[top[u]];
	}
}

std::vector<int> vec;
signed main(){
	n=getint();
	for(int re i=1;i<=n;++i)val[i]=getint();
	for(int re i=2;i<=n;++i)addedge(fa[i]=getint(),i);
	dfs1(1),top[1]=1,dfs2(1);
	build(1,1,n);
	while(true){
		if(mx[1].fi==0)break;
		ans+=mx[1].fi;
		vec.push_back(pos[mx[1].se]);
		find_conquer(vec.back());
		while(true){
			if(mx[1].fi==0)break;
			vec.push_back(pos[mx[1].se]);
			find_conquer(vec.back());
		}
		recover(1,1,n);
		for(int re i=0;i<vec.size();++i)modify(1,1,n,in[vec[i]],0);
		vec.clear();
	}
	cout<<ans;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值