bzoj4458: GTY的OJ

题目大意:给定一棵带点权的有根树,同时给定L,R,要求找M条链,每条链满足以下条件的情况下,要求所有链权和最大:

1、两两不相同(可以包含/相交等)

2、节点数在[L,R]间

3、其中一个端点的深度必须是整条链所有点深度的最小值(原谅我实在不会表达……)(形象地说,就是直上直下)



感觉和NOI某原题什么钢琴有点像

当一条链的下端点确定时,上端点的选择范围就是一条链,也就是说,我们可以求出每个点到根的点权和val[u]存入主席树,这样就可以求 以指定点为下端点 权第k大的链了。

用堆来维护 所有下端点当前权最大的链,每取出一个当前最大值,假设它是其下端点权第k大的链,就在主席树里找这个下端点权第k+1大的链,然后就行了。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#define ll long long
#define N 500005
#define M 500005
#define INF (1e9)

using namespace std;
inline int read(){
	int ret=0;char ch=getchar();
	bool flag=0;
	while (ch<‘0‘||ch>‘9‘){
		flag=ch==‘-‘;
		ch=getchar();
	}
	while (‘0‘<=ch&&ch<=‘9‘){
		ret=ret*10-48+ch;
		ch=getchar();
	}
	return flag?-ret:ret;
}

int n;
int fa[N],f[N][22],fl[N],fr[N],deep[N];
int a[N],val[N];
int need,L,R;
int root[N];

void init(){
	n=read()+1;fa[2]=read()+1;
	for (int i=3;i<=n;++i) fa[i]=read()+1;
	for (int i=2;i<=n;++i) a[i]=read();
	fa[1]=a[1]=0;
	need=read();L=read();R=read()+1;//interval->[L,R)
}

struct SegmentTree{
	struct STnode{
		int sum,ls,rs;
	} t[N*33];
	int size;
	void clear(){size=t[0].sum=t[0].ls=t[0].rs=0;}
	void modify(int &x,int L,int R,int pos){
		t[++size]=t[x];
		x=size;
		++t[x].sum;
		if (L==R) return;
		int mid=(L+R)/2;
		if (L+R<0) --mid;
		if (pos<=mid) modify(t[x].ls,L,mid,pos);
		else modify(t[x].rs,mid+1,R,pos);
	}
	int qmink(int x,int y,int L,int R,int k){
		if (L==R) return L;
		int tmp=t[t[x].ls].sum-t[t[y].ls].sum,mid=(L+R)/2;
		if (L+R<0) --mid;
		if (k<=tmp) return qmink(t[x].ls,t[y].ls,L,mid,k);
		else return qmink(t[x].rs,t[y].rs,mid+1,R,k-tmp);
	}
} st;

void precompute(){
	val[0]=a[0]=deep[0]=fa[0]=0;
	for (int i=1;i<=n;++i){
		val[i]=val[fa[i]]+a[i];
		deep[i]=deep[fa[i]]+1;
		f[i][0]=fa[i];
	}
	memset(f[0],0,sizeof(f[0]));
	for (int k=1;k<=20;++k)
		for (int i=1;i<=n;++i)
			f[i][k]=f[f[i][k-1]][k-1];

	st.clear();root[0]=0;
	for (int i=1;i<=n;++i){
		fl[i]=fr[i]=i;
		for (int k=0;k<=20;++k){
			if ((L&(1<<k))>0) fl[i]=f[fl[i]][k];
			if ((R&(1<<k))>0) fr[i]=f[fr[i]][k];
		}

		st.modify(root[i]=root[fa[i]],-INF,INF,val[i]);
	}
}

struct HeapNode{
	int pos,value,k;
	HeapNode(){}
	HeapNode(int _pos,int _value,int _k):pos(_pos),value(_value),k(_k){}
};
inline bool operator <(const HeapNode &u,const HeapNode &v){
	return u.value<v.value;
}
priority_queue<HeapNode> h;

void work(){
	while (!h.empty()) h.pop();
	for (int i=1;i<=n;++i)
		if (deep[fl[i]]-deep[fr[i]])
			h.push(HeapNode(i,val[i]-st.qmink(root[fl[i]],root[fr[i]],-INF,INF,1),1));
	ll ans=0;
	while (need--){
		HeapNode now=h.top();
		h.pop();
		ans+=(ll)now.value;
		int u=fl[now.pos],v=fr[now.pos];
		if (deep[u]-deep[v]>now.k)
			h.push(HeapNode(now.pos,val[now.pos]-st.qmink(root[u],root[v],-INF,INF,now.k+1),now.k+1));
	}
	printf("%lld\n",ans);
}

int main(){
	init();
	precompute();
	work();
	return 0;
}

  

时间: 2024-10-18 20:40:05

bzoj4458: GTY的OJ的相关文章

LeetCode OJ - Sum Root to Leaf Numbers

这道题也很简单,只要把二叉树按照宽度优先的策略遍历一遍,就可以解决问题,采用递归方法越是简单. 下面是AC代码: 1 /** 2 * Sum Root to Leaf Numbers 3 * 采用递归的方法,宽度遍历 4 */ 5 int result=0; 6 public int sumNumbers(TreeNode root){ 7 8 bFSearch(root,0); 9 return result; 10 } 11 private void bFSearch(TreeNode ro

LeetCode OJ - Longest Consecutive Sequence

这道题中要求时间复杂度为O(n),首先我们可以知道的是,如果先对数组排序再计算其最长连续序列的时间复杂度是O(nlogn),所以不能用排序的方法.我一开始想是不是应该用动态规划来解,发现其并不符合动态规划的特征.最后采用类似于LRU_Cache中出现的数据结构(集快速查询和顺序遍历两大优点于一身)来解决问题.具体来说其数据结构是HashMap<Integer,LNode>,key是数组中的元素,所有连续的元素可以通过LNode的next指针相连起来. 总体思路是,顺序遍历输入的数组元素,对每个

LeetCode OJ - Surrounded Regions

我觉得这道题和传统的用动规或者贪心等算法的题目不同.按照题目的意思,就是将被'X'围绕的'O'区域找出来,然后覆盖成'X'. 那问题就变成两个子问题: 1. 找到'O'区域,可能有多个区域,每个区域'O'都是相连的: 2. 判断'O'区域是否是被'X'包围. 我采用树的宽度遍历的方法,找到每一个'O'区域,并为每个区域设置一个value值,为0或者1,1表示是被'X'包围,0则表示不是.是否被'X'包围就是看'O'区域的边界是否是在2D数组的边界上. 下面是具体的AC代码: class Boar

light oj 1236 【大数分解】

给定一个大数,分解质因数,每个质因子的个数为e1,e2,e3,--em, 则结果为((1+2*e1)*(1+2*e2)--(1+2*em)+1)/2. //light oj 1236 大数分解素因子 #include <stdio.h> #include <iostream> #include <string.h> #include <algorithm> #include <math.h> #include <ctype.h> #i

【华为OJ】201301 JAVA 题目0-1级 将数组分为相等的两组

描述:  编写一个函数,传入一个int型数组,返回该数组能否分成两组,使得两组中各元素加起来的和相等,并且,所有5的倍数必须在其中一个组中,所有3的倍数在另一个组中(不包括5的倍数),能满足以上条件,返回true:不满足时返回false. 知识点: 语言基础,字符串,循环,函数,指针,枚举,位运算,结构体,联合体,文件操作,递归    题目来源: 内部整理  练习阶段: 初级  运行时间限制: 10Sec 内存限制: 128MByte 输入: 输入输入的数据个数 输入一个int型数组 输出: 返

LeetCode OJ - Subsets 1 &amp;&amp; 2

这道题的做法,一定得掌握啊!!!  elegant & beautiful & concise 下面是AC代码: 1 /** 2 * Given a set of distinct integers, S, return all possible subsets. 3 * 这道题的做法应该要记住!!!!! 4 * @param s 5 * @return 6 */ 7 public ArrayList<ArrayList<Integer>> subsets(int[

LeetCode OJ - Convert Sorted Array/List to Binary Search Tree

虚函数使用的时机 为什么虚函数不总是适用? 1. 虚函数有事会带来很大的消耗: 2. 虚函数不总是提供所需的行为: 3. 当我们不考虑继承当前类时,不必使用虚函数. 必须使用虚函数的情况: 1. 当你想删除一个表面上指向基类对象,实际却是指向派生类对象的指针,就需要虚析构函数. LeetCode OJ - Convert Sorted Array/List to Binary Search Tree,布布扣,bubuko.com LeetCode OJ - Convert Sorted Arra

【南阳OJ分类之语言入门】80题题目+AC代码汇总

声明: 题目部分皆为南阳OJ题目. 代码部分包含AC代码(可能不止一个)和最优代码,大部分都是本人写的,并且大部分为c代码和少部分c++代码and极少java代码,但基本都是c语言知识点,没有太多差别,可能代码有的写的比较丑,毕竟知识有限. 语言入门部分题基本都较为简单,是学习编程入门的很好练习,也是ACM的第一步,入门的最佳方法,望认真对待. 本文由csdn-jtahstu原创,转载请注明出处,欢迎志同道合的朋友一起交流学习.本人QQ:1373758426和csdn博客地址. now begi

HDU 5172 GTY&#39;s gay friends (线段树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5172 题意: 给你一个n个数的数组,m次询问,询问在[L, R] 这个区间里面有没有 [1, R-L+1] 的数. 题解: 判断有没有 1 ~ R-L+1 其实就是判断这一段区间和是不是等于 (R-L+1)*(R-L+1+1)/ 2 . 当然还有就是每一个数只能出现1次. 这个问题我们应该怎么解决呢. 我们可以记录第i个数x 的前一个x出现的位置.如果x的前一个x也出现在[L, R]里面,那么这一段