专栏文章

【题解】P12914 [POI 2020/2021 R2] 沙滩游客 / Plażowicze

P12914题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@minaqf7t
此快照首次捕获于
2025/12/01 23:20
3 个月前
此快照最后确认于
2025/12/01 23:20
3 个月前
查看原文

题意

给定一个有 nn 个元素的序列 xx,保证 0=x1<x2<<xn=X0=x_1<x_2<\dots<x_n=X。给定 zz 次询问,每次询问会按照如下规则插入 kk 个新元素:
  • pp 为满足 xi+1xix_{i+1}-x_i 最大的 ii 中的最小值,在 xpx_pxp+1x_{p+1} 中插入 xp+xp+12\displaystyle\frac{x_p+x_{p+1}}{2}
求第 kk 个插入元素的值。

思路

每次操作可以理解为把长度最大的区间劈成两半,考虑朴素做法,设一个大根堆,记 pospos 为某个区间左端点的位置,lenlen 为这个区间的长度,初始放入所有区间。每次取出堆顶的区间,把它劈成两半再放回堆中即可。
然后我们可以按 kk 单调不降排序询问,每次模拟到当前的 kk。这样做时间复杂度为 O(kmaxlog(n+kmax))O(k_{\max}\log (n+k_{\max})),显然无法通过。
由于 kmaxk_{\max} 过大,考虑将其优化掉。我们发现一个区间被劈成两半后的两个区间本质是相同的,所以我们可以记 cntcnt 为这个区间包含几半,lenlen 为每半的长度,pospos 为这个区间的左端点位置。弹出堆顶区间后可以发现必然会从左往右把这个区间的几半再劈开,所以共插入 cntcnt 个新元素,其中第 kk 个被加入的元素值为 pos+len×(k1)+len2pos+len\times(k-1)+\displaystyle\frac{len}{2}。之后我们只需把 lenlen 减半,cntcnt 翻倍,再放入堆中即可。
由于每轮操作最多 NN 次弹出堆顶,最少让堆中所有的 cntcnt 翻倍,所以总的时间复杂度为 O(nlogkmaxlogn)O(n\log k_{\max}\log n)

代码

CPP
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
struct fact{int x,y;};
bool operator<(const fact x,const fact y){return x.x*y.y<y.x*x.y;}
bool operator>(const fact x,const fact y){return x.x*y.y>y.x*x.y;}
bool operator==(const fact x,const fact y){return x.x*y.y==y.x*x.y;}
bool operator!=(const fact x,const fact y){return x.x*y.y!=y.x*x.y;}
struct data1{int k,id;}ask[N];
bool cmp(data1 x,data1 y){return x.k<y.k;}
struct data2
{
    int pos,cnt;
    fact len;
};
bool operator<(const data2 x,const data2 y){return(x.len!=y.len?x.len<y.len:x.pos>y.pos);}
bool operator>(const data2 x,const data2 y){return(x.len!=y.len?x.len>y.len:x.pos<y.pos);}
int a[N];
fact ans[N];
void solve()
{
    int n,len,q;
    cin>>n>>len>>q;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=q;i++)cin>>ask[i].k,ask[i].id=i;
    sort(ask+1,ask+q+1,cmp);
    priority_queue<data2>heap;
    for(int i=2;i<=n;i++)heap.push({a[i-1],1,{a[i]-a[i-1],1}});
    int sum=0;
    for(int i=1;;)
    {
        auto now=heap.top();
        heap.pop();
        fact tmp=now.len;
        if((tmp.x&1)^1)tmp.x>>=1;
        else tmp.y<<=1;
        while(i<=q&&sum+now.cnt>=ask[i].k)
        {
            int id=ask[i].id;
            ans[id]={now.pos*now.len.y+now.len.x*(ask[i].k-sum-1),now.len.y};
            if(now.len.y!=tmp.y)ans[id].x<<=1,ans[id].y<<=1;
            ans[id].x+=tmp.x,i++;
        }
        if(i>q)break;
        heap.push({now.pos,now.cnt<<1,tmp});
        sum+=now.cnt;
    }
    for(int i=1;i<=q;i++)
    {
        int gcd=__gcd(ans[i].x,ans[i].y);
        cout<<ans[i].x/gcd<<'/'<<ans[i].y/gcd<<'\n';
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int _=1;
    for(;_;_--)solve();
    return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...