jzoj6133 [NOI2019模拟2019.4.18]商店 线段树模拟费用流

90 篇文章 0 订阅
33 篇文章 0 订阅

Description


在这里插入图片描述 N , M ≤ 3 e 6 N,M\le3e6 N,M3e6

Solution


求dfs序的时候爆栈了QUQ

考虑人和商品建点跑费用流,优化一下可能可以跑1e5?
观察我们费用流实际上在干什么,就是从一个子树内选出最大的权值然后把它取反。那么我们可以用线段树维护dfs序区间最大值来搞这个东西。由于直接做没法退流因此需要按照dfs序降序贪心
考虑到时限只有1s,nlogn要跑3e6,我们可以用桶排搞掉排序的一个log,然后记录叶子直接向上跳搞掉常数,这样卡一卡就可以过了

题解比较牛逼。我们把询问离线然后打标记,每做完一个节点就把这个节点缩到它父亲所在并查集。又短又快的标准做法

Code


// #pragma GCC optimize(3)
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)
#define drp(i,st,ed) for (register int i=st;i>=ed;--i)

typedef long long LL;
const int INF=0x3f3f3f3f;
const int N=3000005;

struct edge {int y,next;} e[N];

int pos[N],dfn[N],size[N];
int rec[N<<2],r[N],p[N],lxf[N],wjp[N];
int ls[N],fa[N],edCnt,ql,qr,n;
int queue[N];

char buffer[N],*S,*T; 
inline char Get_Char() {
	if(S==T) {
		T=(S=buffer)+fread(buffer,1,N,stdin);
		if(S==T) return EOF;
	}
	return *S++;
}

int read() {
	char c; int re=0;
	for(c=Get_Char();c<'0'||c>'9';c=Get_Char());
	while(c>='0'&&c<='9') re=(re<<1)+(re<<3)+(c-'0'),c=Get_Char();
	return re;
}

void add_edge(int x,int y) {
	e[++edCnt]=(edge) {y,ls[x]}; ls[x]=edCnt;
}

void build(int now,int tl,int tr) {
	if (tl==tr) return (void) (wjp[rec[now]=tl]=now);
	int mid=(tl+tr)>>1;
	build(now<<1,tl,mid),build(now<<1|1,mid+1,tr);
	rec[now]=(dfn[rec[now<<1]]>dfn[rec[now<<1|1]])?rec[now<<1]:rec[now<<1|1];
}

int query(int now,int tl,int tr) {
	if (tl>=ql&&tr<=qr) return rec[now];
	int mid=(tl+tr)>>1,qx,qy;
	qx=(ql<=mid)?query(now<<1,tl,mid):0;
	qy=(mid+1<=qr)?qy=query(now<<1|1,mid+1,tr):0;
	if (!qx||!qy) return qx+qy;
	return (dfn[qx]>dfn[qy])?qx:qy;
}

void bfs() {
	int head,tail;
	queue[head=tail=1]=1;
	for (;head<=tail;) { 
		int x=queue[head++];
		for (int i=ls[x];i;i=e[i].next) queue[++tail]=e[i].y;
	}
	drp(ti,n,1) {
		int x=queue[ti];
		size[x]=1;
		for (int i=ls[x];i;i=e[i].next) {
			size[x]+=size[e[i].y];
		}
	}
	int last=1; pos[1]=1;
	rep(i,2,n) { 
		if (fa[queue[i]]!=fa[queue[i-1]]) last=pos[fa[queue[i]]];
		else last+=size[queue[i-1]]; 
		pos[queue[i]]=last+1; 
	} 
	rep(i,1,n) dfn[pos[i]]=i-1;
}

void dfs(int x) {
	pos[x]=++pos[0],size[x]=1;
	dfn[pos[0]]=x-1;
	for (int i=ls[x];i;i=e[i].next) {
		dfs(e[i].y); size[x]+=size[e[i].y];
	}
}

bool cmp(int x,int y) {
	return pos[x]>pos[y];
}

int main(void) {
	n=read(); int m=read();
	rep(i,2,n) {
		fa[i]=read()+1;
		add_edge(fa[i],i);
	}
	bfs(); build(1,1,n);
	// dfs(1); build(1,1,n);
	LL ans=0;
	rep(i,1,m) r[i]=read()+1;
	rep(i,1,m) lxf[pos[r[i]]]++;
	drp(i,n,1) lxf[i]+=lxf[i+1];
	int ggg=0;
	rep(i,1,m) p[lxf[pos[r[i]]]--]=r[i];
	ggg=0;
	rep(i,1,m) {
		int x=p[i];
		ql=pos[x],qr=pos[x]+size[x]-1;
		int res=query(1,1,n);
		if (dfn[res]<=0) continue;
		ans+=dfn[res]; dfn[res]=-dfn[res];
		for (int now=wjp[res]>>1;now;now>>=1) {
			rec[now]=(dfn[rec[now<<1]]>dfn[rec[now<<1|1]])?rec[now<<1]:rec[now<<1|1];
		}
	}
	printf("%lld\n", ans);
	return 0;
}
信息学奥赛是一项重要的竞赛活动,旨在培养学生的计算机科学和信息技术能力。省选是指在全省范围内举办的预赛,参赛选手需要通过省选才能晋级到更高层次的比赛。在省选中发挥出色的选手将有机会代表本省参加全国信息学奥林匹克竞赛(NOI)。 而noi_pdf-2020.12.29.rar 是一个压缩文件的命名,其中包含了一份关于NOI的PDF文档。这个文件可能包含了有关NOI的相关资料,例如竞赛规则、题目类型、考试要求等等。通过研究这个文件,选手可以更好地准备信息学奥赛,提高竞赛成绩。 对于想要参加信息学奥赛的同学们来说,可以利用这份PDF文档来深入了解NOI的要求和考试内容。首先,可以仔细阅读竞赛规则,了解比赛的时间、地点、参赛资格等重要信息。其次,可以通过研究题目类型和考试要求,明确自己需要学习和复习的内容,制定合理的备考计划。此外,可以通过查阅往年的竞赛题目和解答,进行练习和模拟考试,提高解题能力和应变能力。 综上所述,信息学奥赛省选和noi_pdf-2020.12.29.rar对于想要参加信息学奥赛的同学们来说都有重要的意义。省选是选拔出优秀选手的一个重要阶段,而noi_pdf-2020.12.29.rar则提供了有关NOI的重要资料,帮助选手更好地准备竞赛。希望通过努力学习和准备,同学们可以在比赛中取得优异成绩,提升自己的计算机科学和信息技术能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值