zoukankan      html  css  js  c++  java
  • 【洛谷P3466】KLOBuilding blocks

    题目

    题目链接:https://www.luogu.com.cn/problem/P3466
    给出一个数列,将一个数加一或减一都付出\(1\)的代价。求最小代价使得数列中存在一个长度为\(m\)的子序列每一个数字都相同。

    思路

    很明显就是枚举每一个长度为\(m\)的区间,然后求中位数搞一搞。
    求中位数可以用对顶堆。我们求出中位数后,计算小于中位数的数字之和以及大于中位数的数字之和,就可以计算出答案。
    但是这样我们从区间\([i,i+m-1]\)转移到\([i+1,i+m]\)时,需要支持在堆中 删除任意元素,而这是\(STL\)自带的堆没有的功能。而又不想手写堆(
    所以我们需要一个能在\(O(\log n)\)的时间复杂度内支持求最值,插入,删除的数据结构。
    那么平衡树就可以了。
    用平衡树代替堆,维护两棵平衡树即可。
    当然这是比较暴力的方法,维护一棵平衡树也是可以的,因为平衡树支持查找中位数(大概吧)。
    时间复杂度\(O(n\log m)\)
    其实写这道题就是想默一下Treap的板子233

    代码

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int N=800010,Inf=1e9;
    int n,m,pos,v,a[N];
    ll sum,ans=1e17;
    
    struct Treenode
    {
    	int lc,rc,size,dat,val,cnt;
    };
    
    struct Treap
    {
    	Treenode tree[N];
    	int tot,root;
    	ll sum;
    	
    	int New(int x)
    	{
    		tree[++tot].val=x;
    		tree[tot].size=tree[tot].cnt=1;
    		tree[tot].dat=rand();
    		return tot;
    	}
    	
    	void pushup(int x)
    	{
    		tree[x].size=tree[tree[x].lc].size+tree[tree[x].rc].size+tree[x].cnt;
    	}
    	
    	void build()
    	{
    		root=New(-Inf);
    		tree[1].rc=New(Inf);
    		pushup(1);
    	}
    	
    	void zig(int &x)
    	{
    		int y=tree[x].lc;
    		tree[x].lc=tree[y].rc; tree[y].rc=x; x=y;
    		pushup(tree[x].rc); pushup(x);
    	}
    	
    	void zag(int &x)
    	{
    		int y=tree[x].rc;
    		tree[x].rc=tree[y].lc; tree[y].lc=x; x=y;
    		pushup(tree[x].lc); pushup(x);
    	}
    	
    	int Min(int x)  //求平衡树中最小值,最大值下同
    	{
    		if (!x) return Inf;
    		if (tree[x].val==-Inf) return Min(tree[x].rc);
    		return min(Min(tree[x].lc),tree[x].val);
    	}
    	
    	int Max(int x)
    	{
    		if (!x) return -Inf;
    		if (tree[x].val==Inf) return Max(tree[x].lc);
    		return max(Max(tree[x].rc),tree[x].val);
    	}
    	
    	void insert(int &x,int val)
    	{
    		if (x==root) sum+=val;
    		if (!x) x=New(val);
    		else if (tree[x].val==val) tree[x].cnt++;
    		else if (val<tree[x].val)
    		{
    			insert(tree[x].lc,val);
    			if (tree[tree[x].lc].dat<tree[x].dat) zig(x);
    		}
    		else
    		{
    			insert(tree[x].rc,val);
    			if (tree[tree[x].rc].dat<tree[x].dat) zag(x);
    		}
    		pushup(x);
    	}
    	
    	void del(int &x,int val)
    	{
    		if (x==root) sum-=val;
    		if (tree[x].val==val)
    		{
    			if (tree[x].cnt>1) tree[x].cnt--;
    			else if (tree[x].lc || tree[x].rc)
    			{
    				if (!tree[x].lc || tree[tree[x].lc].dat<tree[tree[x].rc].dat)
    					zag(x),del(tree[x].lc,val);
    				else
    					zig(x),del(tree[x].rc,val);
    			}
    			else x=0;
    		}
    		else if (tree[x].val>val) del(tree[x].lc,val);
    		else del(tree[x].rc,val);
    		pushup(x);
    	}
    	
    	bool find(int x,int val)  //查找平衡树中是否有val这个值
    	{
    		if (!x) return 0;
    		if (tree[x].val==val) return 1;
    		if (tree[x].val>val) return find(tree[x].lc,val);
    			else return find(tree[x].rc,val);
    	}
    }Treap1,Treap2;
    
    void update()  //维护两棵。。。对顶平衡树?(雾
    {
    	while (Treap1.Max(Treap1.root)>Treap2.Min(Treap2.root))
    	{
    		int x=Treap1.Max(Treap1.root);
    		Treap1.del(Treap1.root,x); Treap2.insert(Treap2.root,x);
    	}
    	while (Treap1.tree[Treap1.root].size-Treap2.tree[Treap2.root].size>=2)
    	{
    		int x=Treap1.Max(Treap1.root);
    		Treap1.del(Treap1.root,x); Treap2.insert(Treap2.root,x);
    	}
    	while (Treap2.tree[Treap2.root].size>Treap1.tree[Treap1.root].size)
    	{
    		int x=Treap2.Min(Treap2.root);
    		Treap2.del(Treap2.root,x); Treap1.insert(Treap1.root,x);
    	}
    }
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	srand(n*2333-m*666);  //优秀的重置种子方法
    	for (int i=1;i<=n;i++)
    		scanf("%d",&a[i]);
    	Treap1.build(); Treap2.build();
    	for (int i=1;i<m;i++)
    	{
    		Treap1.insert(Treap1.root,a[i]);
    		update();
    	}
    	for (int i=m;i<=n;i++)
    	{
    		Treap1.insert(Treap1.root,a[i]);
    		update();
    		ll mid=1LL*Treap1.Max(Treap1.root);
    		int cnt1=Treap1.tree[Treap1.root].size,cnt2=Treap2.tree[Treap2.root].size;
    		ll cost=mid*cnt1-Treap1.sum+Treap2.sum-mid*cnt2;  //计算代价
    		if (cost<ans)
    			ans=cost,pos=i-m+1,v=mid;
    		if (Treap1.find(Treap1.root,a[i-m+1])) Treap1.del(Treap1.root,a[i-m+1]);
    			else Treap2.del(Treap2.root,a[i-m+1]);
    		update();
    	}
    	printf("%lld\n",ans);
    	for (int i=1;i<=n;i++)
    		if (i>=pos && i<pos+m) printf("%d\n",v);
    			else printf("%d\n",a[i]);
    	return 0;
    }
    
  • 相关阅读:
    mybatis <=或这个>=提示错误Tag name expecte问题解决
    Navicat 设置自增长初始值
    mysql 时间与字符串相互转换
    jquery 动态控制显隐
    mysql 查询常见时间段数据
    jquery 取得select选中的值
    java8 运算语法集
    springboot 单元测试
    idea 自动生成并跳转单元测试
    限制页面被pc端访问
  • 原文地址:https://www.cnblogs.com/stoorz/p/12237714.html
Copyright © 2011-2022 走看看