Codeforces 700D Huffman Coding on Segment 莫队算法+哈夫曼树

题意

给一个长度为n的序列,每次询问把一个区间内的数转换成哈夫曼编码需要的最小长度。哈夫曼编码就是把每个字符映射到一个01串,要求对于两个不同的字符,其中一个的编码不能是另一个的前缀。
n , a i , q 100000

分析

显然我们可以把每个字符出现的次数看作是权值,然后建一棵哈夫曼树的代价即为答案。
用莫队维护每个数的出现次数,然后对于出现次数不大于 n 的,显然只有 O ( n ) 种不同的数,用双指针合并就好了。
然后对于出现次数大于 n 的,直接做一次哈夫曼树就好了。
时间复杂度 O ( n n l o g n )

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<vector>

typedef long long LL;

const int N=100005;

int n,m,B,bel[N],a[N],t[N],c[N],d[N];
LL ans[N];
struct data{int l,r,id;}q[N];
std::priority_queue<int> que;
std::vector<int> vec;

int read()
{
    int 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;
}

bool cmp(data a,data b)
{
    return bel[a.l]<bel[b.l]||bel[a.l]==bel[b.l]&&((bel[a.l]&1)?(a.r<b.r):(a.r>b.r));
}

void updata(int x,int y)
{
    if (t[a[x]]<=B) d[t[a[x]]]--;
    t[a[x]]+=y;
    if (t[a[x]]<=B) d[t[a[x]]]++;
}

LL query()
{
    int p1=1,p2=1;LL ans=0;
    for (int i=0;i<vec.size();i++)
        if (t[vec[i]]>B) que.push(-t[vec[i]]);
    for (int i=1;i<=B;i++) c[i]=d[i];
    while (p1<=B)
    {
        while (p1<=B&&!c[p1]) p1++;
        while (p2<=B&&(!c[p2]||c[p2]==1&&p1==p2)) p2++;
        if (p1>B||p2>B) break;
        int s=(p1==p2)?c[p1]/2:std::min(c[p1],c[p2]);
        ans+=(LL)(p1+p2)*s;
        c[p1]-=s;c[p2]-=s;
        if (p1+p2>B) for (int i=1;i<=s;i++) que.push(-p1-p2);
        else c[p1+p2]+=s;
    }
    if (c[p1]) que.push(-p1);
    while (que.size()>1)
    {
        int x=que.top();que.pop();
        int y=que.top();que.pop();
        ans-=x+y;
        que.push(x+y);
    }
    while (!que.empty()) que.pop();
    return ans;
}

void solve()
{
    for (int i=1,l=1,r=0;i<=m;i++)
    {
        for (;r<q[i].r;r++) updata(r+1,1);
        for (;l>q[i].l;l--) updata(l-1,1);
        for (;r>q[i].r;r--) updata(r,-1);
        for (;l<q[i].l;l++) updata(l,-1);
        ans[q[i].id]=query();
    }
}

int main()
{
    n=read();B=sqrt(n);
    for (int i=1;i<=n;i++) a[i]=read(),bel[i]=(i+B-1)/B,t[a[i]]++;
    for (int i=1;i<=100000;i++)
        if (t[i]>B) vec.push_back(i);
    for (int i=1;i<=n;i++) t[a[i]]--;
    m=read();
    for (int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].id=i;
    std::sort(q+1,q+m+1,cmp);
    solve();
    for (int i=1;i<=m;i++) printf("%lld\n",ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_33229466/article/details/80782067