bzoj1112 [POI2008]砖块Klo

本文介绍如何使用平衡树(Treap)解决N柱砖问题,目标是在最少的动作次数下,使任意连续K柱的高度一致。通过维护一段长度为k的区间,将该区间内的砖块高度调整为中位数,从而最小化整体的操作次数。

 

Description

N柱砖,希望有连续K柱的高度是一样的. 你可以选择以下两个动作 1:从某柱砖的顶端拿一块砖出来,丢掉不要了. 2:从仓库中拿出一块砖,放到另一柱.仓库无限大. 现在希望用最小次数的动作完成任务.

Input

第一行给出N,K. (1 ≤ k ≤ n ≤ 100000), 下面N行,每行代表这柱砖的高度.0 ≤ hi ≤ 1000000

Output

最小的动作次数

Sample Input

5 3
3
9
2
3
1

Sample Output

2

HINT

原题还要求输出结束状态时,每柱砖的高度.本题略去.

 

很容易想到用个平衡树维护一段长为k的区间,每次区间右移就是加一个数再删一个数。

题意是把区间全变成一个数,然后求Σ|a[i]-x|

首先,把一段区间全变成中位数是最优的。这个可以自行脑补(一开始自己yy是平均数结果各种呵呵呵)

ndsf:splay好做啊

但是……总之我就是要用treap

求中位数x简单,第(k/2+1)大就是了(其实不太放心然后x+1再跑了一遍,其实不+1应该也没错吧……)

然后abs要分类讨论,要比它大的统计一次,比他小的统计一次

我的做法是直接自顶向下递归找一下,就是细节啦

以统计比它大的(upcalc(now,x))为例

递归搜到一个节点,如果这个点比它小,或者和它相等,那么左子树和这个点都不用统计,只要处理它的右子树就好了

如果这个点比它大,显然这个点和它的右子树都肯定比它大,直接推公式统计。左子树未定,递归搜下去

第一次写维护平衡树的sum就写疵了……各种不知道哪里写错……一怒之下treap节点几个变量改longlong结果A了……

#include<cstdio>
#include<cstdlib>
#include<ctime>
#define LL long long
struct treap{
	int l,r,dat,rnd;
	LL rep,son;
	LL sum;
}tree[500000];
int a[100010];
int n,k,treesize,root;
LL tot,ans=1LL<<40;
inline LL min(LL a,LL b)
{return a<b?a:b;}
inline void update(int k)
{
	tree[k].sum=(LL)tree[tree[k].l].sum+tree[tree[k].r].sum+tree[k].dat*tree[k].rep;
	tree[k].son=tree[tree[k].l].son+tree[tree[k].r].son+tree[k].rep;
}
inline void right_rotate(int &k)
{
	int t=tree[k].l;
	tree[k].l=tree[t].r;
	tree[t].r=k;
	tree[t].sum=tree[k].sum;
	tree[t].son=tree[k].son;
	update(k);
	k=t;
}
inline void left_rotate(int &k)
{
	int t=tree[k].r;
	tree[k].r=tree[t].l;
	tree[t].l=k;
	tree[t].sum=tree[k].sum;
	tree[t].son=tree[k].son;
	update(k);
	k=t;
}
inline void insert(int &now,int x)
{
	if (!now)
	{
		now=++treesize;
		tree[now].rnd=rand();
		tree[now].son=tree[now].rep=1;
		tree[now].dat=x;
		tree[now].sum=(LL)x;
		return;
	}
	tree[now].son++;
	int data=tree[now].dat;
	if (x==data)
	{
		tree[now].rep++;
		tree[now].sum+=tree[now].dat;
		return;
	}
	if (x<data)
	{
		insert(tree[now].l,x);
		if (tree[tree[now].l].rnd>tree[now].rnd)right_rotate(now);
	}else
	{
		insert(tree[now].r,x);
		if (tree[tree[now].r].rnd>tree[now].rnd)left_rotate(now);
	}
	update(now);
}
inline void del(int &now,int x)
{
	if (!now)return;
	int num=tree[now].dat;
	if (x==num)
	{
		if (tree[now].rep>1)
		{
			tree[now].rep--;
			tree[now].son--;
			tree[now].sum-=tree[now].dat;
			return;
		}
		if (tree[now].l*tree[now].r==0)now=tree[now].l+tree[now].r;
		else
		{
			if (tree[tree[now].l].rnd>tree[tree[now].r].rnd)
			{
				right_rotate(now);
				del(now,x);
			}else
			{
				left_rotate(now);
				del(now,x);
			}
		}
	}else
	{
		tree[now].son--;
    	if (x<num)del(tree[now].l,x);
		else del(tree[now].r,x);
	}
	update(now);
}
inline int find(int now,int x)
{
    if(!now)return 0;
    if(x<=tree[tree[now].l].son)return find(tree[now].l,x);
    else if(x>tree[tree[now].l].son+tree[now].rep)
	  return find(tree[now].r,x-tree[tree[now].l].son-tree[now].rep);
    else return tree[now].dat;
}
inline void upcalc(int now,int x)
{
	if (!now)return;
	if (tree[now].dat<=x)upcalc(tree[now].r,x);
	else
	{
		tot+=(LL)(tree[now].sum-tree[tree[now].l].sum)-x*(tree[now].son-tree[tree[now].l].son);
		upcalc(tree[now].l,x);
	}
}
inline void dncalc(int now,int x)
{
	if (!now)return;
	if (tree[now].dat>=x)dncalc(tree[now].l,x);
	else
	{
		tot+=(LL)x*(tree[now].son-tree[tree[now].r].son)-(tree[now].sum-tree[tree[now].r].sum);
		dncalc(tree[now].r,x);
	}
}
inline void calc(int now,int todel,int toadd)
{
	if(todel!=toadd)
	{
		insert(root,toadd);
		del(root,todel);
	}
	int ave;
	ave=find(root,k/2+1);
	tot=0;
	upcalc(root,ave);
	dncalc(root,ave);
	ans=min(ans,tot);
	ave++;
	tot=0;
	upcalc(root,ave);
	dncalc(root,ave);
	ans=min(ans,tot);
}	
inline LL read()
{
    LL x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int main()
{
	n=read();k=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=k;i++)insert(root,a[i]);
	calc(k,0,0);
	for(int i=k+1;i<=n;i++)calc(i,a[i-k],a[i]);
	printf("%lld",ans);
}

  

转载于:https://www.cnblogs.com/zhber/p/4035927.html

题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (树形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用树形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵树,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 树形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值