bzoj 1112 poi 2008 砖块

这滞胀题调了两天了...

好愚蠢的错误啊...

其实这道题思维比较简单,就是利用treap进行维护(有人说线段树好写,表示treap真心很模板)

就是枚举所有长度为k的区间,查出中位数,计算代价即可。

(根据绝对值不等式的几何意义,中位数一定是最优解)

而维护长度为k的区间也很简单,就是首先把前k个扔到树上,然后每次把新来的插入,把最前面的一个删除即可

至于求中位数,简直就是基础操作嘛

关键在于...代价怎么算?

显然我们不能把所有数枚举出来挨个加减,这样会T飞的...

所以我们考虑直接在treap上维护,根据treap很重要的性质:左树<根<右树

那么我们对每个节点,维护一个子树权值和,这样就可以做到在查询中位数的同时查出小于中位数的数之和和大于中位数的数之和了

注意一个小细节,就是在查询的时候,要把重复出现的中位数分左右放到左右的和里,否则计算会有bug

剩下的就是模板了

不要像我一样,插点不修改树的大小,输出全是负数...

贴代码(巨丑)

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ls tree[rt].lson
#define rs tree[rt].rson
#define ll long long
using namespace std;
struct Treap
{
    int lson;
    int rson;
    int huge;
    int same;
    ll val;
    int rank;
    ll sum;
}tree[100005];
int a[100005];
int tot=0;
int n,k,mid;
ll s[100005];
int rot=0;
inline int read()
{
    int f=1,x=0;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;
}
void update(int rt)
{
    tree[rt].huge=tree[ls].huge+tree[rs].huge+tree[rt].same;
    tree[rt].sum=(ll)tree[ls].sum+(ll)tree[rs].sum+(ll)tree[rt].same*(ll)tree[rt].val;
}
void lturn(int &rt)
{
    int temp=rs;
    rs=tree[rs].lson;
    tree[temp].huge=tree[rt].huge;
    tree[temp].sum=tree[rt].sum;
    tree[temp].lson=rt;
    update(rt);
    rt=temp;
}
void rturn(int &rt)
{
    int temp=ls;
    ls=tree[ls].rson;
    tree[temp].huge=tree[rt].huge;
    tree[temp].rson=rt;
    tree[temp].sum=tree[rt].sum;
    update(rt);
    rt=temp;
}
void ins(int &rt,ll v)
{
    if(!rt)
    {
        rt=++tot;
        tree[rt].huge=1;
        tree[rt].same=1;
        tree[rt].val=v;
        tree[rt].rank=rand();
        tree[rt].sum=v;
        return;
    }
    tree[rt].sum+=v;
    tree[rt].huge++;
    if(tree[rt].val==v)
    {
        tree[rt].same++;
        return;
    }
    if(tree[rt].val>v)
    {
        ins(ls,v);
        if(tree[ls].rank<tree[rt].rank)
        {
            rturn(rt);
        }
    }else
    {
        ins(rs,v);
        if(tree[rs].rank<tree[rt].rank)
        {
            lturn(rt);
        }
    }
}
void del(int &rt,ll v)
{
    if(!rt)
    {
        return;
    }
    if(tree[rt].val==v)
    {
        if(tree[rt].same>1)
        {
            tree[rt].huge--;
            tree[rt].same--;
            tree[rt].sum-=(ll)v;
            return;
        }else if(ls*rs==0)
        {
            rt=ls+rs;
            return;
        }else
        {
            if(tree[ls].rank<tree[rs].rank)
            {
                rturn(rt);
                del(rt,v);
            }else
            {
                lturn(rt);
                del(rt,v);
            }
        }
        return;
    }
    tree[rt].huge--;
    tree[rt].sum-=v;
    if(tree[rt].val>v)
    {
        del(ls,v);
    }else
    {
        del(rs,v);
    }
    update(rt);
}
ll Lsum,Rsum;
int tt;
int query_num(int rt,int v)
{
    if(!rt)
    {
        return 0;
    }
    if(tree[ls].huge>=v)
    {
        Rsum+=(ll)tree[rs].sum+(ll)tree[rt].same*(ll)tree[rt].val;
        return query_num(ls,v);
    }else if(tree[ls].huge+tree[rt].same<v)
    {
        Lsum+=(ll)tree[ls].sum+(ll)tree[rt].val*(ll)tree[rt].same;
        return query_num(rs,v-tree[ls].huge-tree[rt].same);
    }else
    {
        Lsum+=(ll)tree[ls].sum+(ll)(v-tree[ls].huge-1)*(ll)tree[rt].val;
        Rsum+=(ll)tree[rs].sum+(ll)(tree[ls].huge+tree[rt].same-v)*(ll)tree[rt].val;
        return tree[rt].val;
    }
}
int main()
{
    n=read(),k=read();
    mid=(k+1)/2;
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
    }
    for(int i=1;i<=k;i++)
    {
        ins(rot,a[i]);
    }
    int lret=1,rret=k;
    int v0=query_num(rot,mid);
    int ret=v0;
    ll co=(ll)(mid-1)*(ll)v0-Lsum+Rsum-(ll)(k-mid)*(ll)v0;
    for(int i=k+1;i<=n;i++)
    {
        int st=i-k+1;
        del(rot,a[st-1]);
        ins(rot,a[i]);
        tt=0,Lsum=0,Rsum=0;
        int v1=query_num(rot,mid);
        ll temp=(ll)(mid-1)*(ll)v1-Lsum+Rsum-(ll)(k-mid)*(ll)v1;
        if(co>temp)
        {
            co=temp;
            lret=st;
            rret=i;
            ret=v1;
        }
    }
    printf("%lld\n",co);
    for(int i=1;i<=n;i++)
    {
        if(i<lret||i>rret)
        {
            printf("%d\n",a[i]);
        }else
        {
            printf("%d\n",ret);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zhangleo/p/9650836.html

时间: 2024-10-13 06:17:22

bzoj 1112 poi 2008 砖块的相关文章

BZOJ 1132 POI 2008 Tro 计算几何

题目大意:给出平面上的一些点,问这些点中的任意三个点组成的三角形的面积和是多少. 思路:看数据范围只算法系列.由于每个三角形有三个顶点,因此暴力的话应该是O(n^3)的时间复杂度,很明显超时了,但是我们只需要将它优化到O(n^2logn)就可以解决了. 好吧,剩下的随便猜一猜,比如O(n^2)的枚举,然后剩下的logn什么也干不了... 再比如O(n)的枚举,然后剩下O(nlogn)排序... 好像有戏啊.. 枚举每一个点,计算以这个点为坐标原点,在第一象限的所有点与原点组成的三角形的面积和.计

[BZOJ 1112] [POI2008] 砖块Klo 【区间K大】

题目链接:BZOJ - 1112 题目分析 枚举每一个长度为k的连续区间,求出这个区间的最优答案,更新全局答案. 可以发现,这个区间的所有柱子最终都变成这k个数的中位数时最优,那么我们就需要查询这个区间的中位数了. 找到中位数之后,我们还应该求出这个区间内小于中位数的数的和,大于中位数的数的和,从而求出操作步数. 这些需要求的值可以用线段树或平衡树来写,我写的是线段树,但是实际上这是一道POI的题目,在MAIN上的空间限制只有35MB,线段树应该是不行的. 因为平衡树只需要 O(n) 空间,所以

[POI 2008][BZOJ 1132]Tro

这题我真是无能为力了 这题的做法还是挺简单的 枚举左下角的点做为原点,把其余点按极角排序    PS.是作为原点,如枚举到 k 时,对于所有 p[i] (包括p[k]) p[i]-=p[k] (此处为向量减法) 排序后满足 i<j 的两个向量 p[i] 和 p[j] 的叉积都是正数了 ΣΣp[i]×p[j] = ΣΣ(p[i].x*p[j].y-p[i].y*p[j].x) = Σ(p[i].x*Σp[j].y)-Σ(p[i].y*Σp[j].x) 计算叉积和的复杂度就从 O(n2) 降为了 O

[POI 2008]Mafia

这题目写了我好长时间,但还是几乎(不要在意细节)一遍 A 了喵~ 据说有代码奇短的,Orz 思路巧妙的大爷 想我这种 Brute Force 写写的傻 X 真是代码量飞起来了耶,喵~ 如果把每个人看成点,每个人要 kill 的人向此人连出一条有向边,那么每个点仅有一条出边和一条入边 经验告诉我们,这就是 环+内向图 的节奏 经验又告诉我,处理这种图要么先暴力搞环,再搞挂在环上的树.要么先搞树,再弄环. 此题显然是后者 环+内向图只需要用 bfs 就可以搞出来了,看到写 tarjan 的真是 Or

BZOJ 1112: [POI2008]砖块Klo

1112: [POI2008]砖块Klo Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1736  Solved: 606[Submit][Status][Discuss] Description N柱砖,希望有连续K柱的高度是一样的. 你可以选择以下两个动作 1:从某柱砖的顶端拿一块砖出来,丢掉不要了. 2:从仓库中拿出一块砖,放到另一柱.仓库无限大. 现在希望用最小次数的动作完成任务. Input 第一行给出N,K. (1 ≤ k ≤ n ≤

BZOJ 1112: [POI2008]砖块Klo1112( BST )

枚举每个长度为k的区间, 然后用平衡树找中位数进行判断, 时间复杂度O(nlogn). 早上起来精神状态不太好...连平衡树都不太会写了...果断去看了会儿番然后就A了哈哈哈 -------------------------------------------------------------------------- #include<bits/stdc++.h> #define rep(i, n) for(int i = 0; i < n; i++) #define clr(x,

BZOJ 1112 [POI2008]砖块Klo(可持久化线段树)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1112 [题目大意] 给出一个数列,对于一个操作,你可以对一个数+1,或者一个数-1, 问若使得数列中出现长度为m的连续相同的数,最少需要的操作数. [题解] 我们发现对于固定区间求最小操作,等价于求区间中数距离中位数差值和最小, 我们发现区间中位数可以利用主席树求区间kth来实现, 同时在主席树上维护权值线段树的区间真值和,那么对于每个区间中的数, 就能分别维护比中位数小的部分的和以

[BZOJ 3747] [POI 2015] Kinoman【线段树】

Problem Link : BZOJ 3747 题解:ZYF-ZYF 神犇的题解 解题的大致思路是,当区间的右端点向右移动一格时,只有两个区间的左端点对应的答案发生了变化. 从 f[i] + 1 到 i 的区间中的答案增加了 W[A[i]], 从 f[f[i]] + 1 到 f[i] 的区间的答案减少了 W[A[i]] ,其余区间的答案没有发生变化. 那么就是线段树的区间修改和区间最值查询. 代码如下: #include <iostream> #include <cstdio>

[BZOJ 1008] [HNOI 2008] 越狱

1008: [HNOI2008]越狱 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 5041  Solved: 2177[Submit][Status][Discuss] Description 监狱有连续编号为1...N的N个房间,每个房间关押一个犯人,有M种宗教,每个犯人可能信仰其中一种.如果相邻房间的犯人的宗教相同,就可能发生越狱,求有多少种状态可能发生越狱 Input 输入两个整数M,N.1<=M<=10^8,1<=N<=10