首页 > 其他分享 >P5012 水の数列 题解

P5012 水の数列 题解

时间:2023-06-05 13:34:33浏览次数:44  
标签:得分 res 数列 lastans int 题解 P5012 siz

水の数列

题目大意

对于给定的数列 \(a\),选择一个数 \(x\),定义其得分为数列中所有小于等于 \(x\) 的数形成的若干个连续区间的平方和除以 \(x\) 所得到的数。

现进行多次询问,每次询问给定两个数 \(l,r\),要求出一个得分最大的 \(x\),满足数列中所有小于等于 \(x\) 的数形成的若干个连续区间的个数在 \(l\) 到 \(r\) 之间。

强制在线。

思路分析

容易发现,满足条件的得分最大的 \(x\) 一定是数列中的一个数。考虑反证法,如果满足条件的得分最大的 \(x\) 不是数列中的数,那么一定可以将 \(x\) 变成数列中最大的小于 \(x\) 的数,分子不变,分母变小,得分增大。

那么这就意味着 \(x\) 的取值只有 \(O(n)\) 级别,又因为得分没有单调性(容易构造,比如 1 100 4 3),所以考虑从小到大枚举 \(x\) 的取值。

由于 \(x\) 单调,那么数列中被标记的数的个数也一定单调,那么就可以用并查集维护所有小于等于 \(x\) 的数形成的连通块,\(O(n\log n)\) 预处理出对于 \(x\) 的每一个取值,得到的得分和连通块的块数。

具体的说,先将原数列排序,从小到大枚举 \(x\)(这里指的是枚举 \(x\) 的可能取值),对于每一个数列中等于 \(x\) 的数,判断是否与其左右的数合并即可。合并时可以 \(O(1)\) 更新信息。

对于每一个询问,我们发现,在预处理时,已经得到了每一个 \(x\) 所对应的区间个数。那么我们可以对于每一个区间长度的所有可能的 \(x\) 所对应的得分取一个最值,再对于区间长度的得分 RMQ 即可。

时间复杂度由 RMQ 的数据结构决定,但不低于 \(O(n\log n)\)。

  • 一些细节

数据在解密时可能爆 long long,可以开 __int128 或读入后先取模。

在无解时输出 -1 -1 后将 lastans 设为 \(1\)。

初始时 lastans 为 \(0\)。

代码

#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;
const int N=1001000,M=1010;
typedef long long ll;

int T,n,tot,S,m,in1,in2,in3,in4;
int fa[N],siz[N],a[N],c[N];
int maxn[N],maxi[M],l[M],r[M],pos[N];//空间一定要开够
ll now,ans[N],lastans;

vector <int> v[N];

int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void merge(int x,int y){
    x=find(x);y=find(y);
    if(x==y) return ;
    now-=1ll*siz[x]*siz[x]+siz[y]*siz[y];//去除贡献
    tot--;siz[x]+=siz[y];fa[y]=x;
    now+=1ll*siz[x]*siz[x];//增加贡献
}

bool compare(int x,int y){return 1ll*ans[x]*c[y]<1ll*ans[y]*c[x];}//分数比较大小,c[x] 是实际的 x,ans[x] 是得分的分子

void query_t(int &res,int x,int y,int g[]){
    for(int i=x;i<=y;i++)
        if(compare(res,g[i])) res=g[i];
}

int query(int x,int y){//RMQ
    int res=0;
    if(pos[x]==pos[y]){query_t(res,x,y,maxn);return res;}
    query_t(res,x,r[pos[x]],maxn);
    query_t(res,l[pos[y]],y,maxn);
    query_t(res,pos[x]+1,pos[y]-1,maxi);
    return res;
}

int main(){
    memset(l,0x3f,sizeof l);
    scanf("%d%d",&n,&T);
    S=sqrt(n);m=n;//RMQ 使用分块
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        c[i]=a[i];
        pos[i]=(i-1)/S+1;//每个数所在块
    }
    for(int i=1;i<=n;i++){//初始化块端点和并查集
        fa[i]=i;siz[i]=1;
        l[pos[i]]=min(l[pos[i]],i);
        r[pos[i]]=max(r[pos[i]],i);
    }
    sort(c+1,c+m+1);//离散化
    m=unique(c+1,c+m+1)-c-1;
    for(int i=1;i<=n;i++){
        a[i]=lower_bound(c+1,c+m+1,a[i])-c;
        v[a[i]].push_back(i);//对于每一个离散化后的值开一个 vector 存下标
    }
    ans[0]=-1;
    c[0]=1;
    for(int x=1;x<=m;x++){
        now+=v[x].size();tot+=v[x].size();//等于 x 的数有 v[x].size() 个
        //now 表示平方和,tot 表示个数
        for(auto it:v[x]){//遍历所有等于 x 的数
            if(it!=1&&a[it-1]<=x) merge(it,it-1);//特判边界
            if(it!=n&&a[it+1]<=x) merge(it,it+1);//如果旁边的数 <= x 就一定被标记或即将被标记
        }
        ans[x]=now;
        if(compare(maxn[tot],x)){
            maxn[tot]=x;//更新单点答案
            if(compare(maxi[pos[tot]],x))
                maxi[pos[tot]]=x;//更新块内答案
        }
    }
    while(T--){
        scanf("%d%d%d%d",&in1,&in2,&in3,&in4);
        in1%=n;in2%=n;in3%=n;in4%=n;//读入后取模
        int l=(1ll*in1*lastans%n+in3-1+n)%n+1;
        int r=(1ll*in2*lastans%n+in4-1+n)%n+1;
        if(l>r) swap(l,r);
        int res=query(l,r);
        if(!res){printf("-1 -1\n%d %d %lld\n",l,r,lastans);lastans=1;}
        else{
            printf("%lld %d\n%d %d %lld\n",ans[res],c[res],l,r,lastans);
            lastans=1ll*(ans[res]%n)*(c[res]%n)%n;
        }
    }
    return 0;
}

标签:得分,res,数列,lastans,int,题解,P5012,siz
From: https://www.cnblogs.com/TKXZ133/p/17457558.html

相关文章

  • [ABC201D] Game in Momotetsu World 题解
    GameinMomotetsuWorld题目大意在一个\(n\timesm\)的网格中,存在红色和蓝色两种格子,红色格子用-表示,蓝色格子用+表示。现在Takahashi和Aoki要在这个网格中进行游戏,游戏的规则如下:初始时,有一枚棋子摆放在左上角,即\((1,1)\)的位置。两名玩家的分数均为\(0\)。......
  • P2048 超级钢琴 题解
    超级钢琴题目大意求出序列中长度在\([L,R]\)中的所有区间的区间和前\(k\)大的区间的区间和。思路分析暴力做法是把所有符合条件的区间扔进堆里,再弹出\(k\)个,时间复杂度\(O((n^2+k)\logn)\),可以拿到\(20\text{pts}\)的好成绩。但真的有必要全部加进去吗?不!我们设五......
  • P5445 路灯 题解
    路灯题目大意在\(n+1\)个站点之间有\(n\)盏路灯,给定\(0\)时刻所有路灯的亮灭情况,在接下来的\(q\)个时刻,每时刻会发生以下两种事件之一:切换某一盏路灯的亮灭。询问两点之间存在多少时刻使得两点之间的路灯全部亮起。思路分析一道不错的数据结构。首先分析题目......
  • Mysql 主从备份 Last_Errno: 1146 Last_Error: Error executing row event: 错误问题
    本人在做主从备份的时候发现了此问题! 1主数据库是已经把这个表删除了丛数据库也是没有备份这个表但是一直报这个错原因是bin-log日志有这个表 但是没记录到已经把这个表删除了 主从表同步实际从库是根据主库的bin-log二进制的SQL进行执行的 这是Mysql的一个BUG1......
  • erlang实现长连接管理问题解决
    具体参见:http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-1/1.    erlang进程增加了休眠特性hibernate,支持连接从20w->70w;如上面的文章里面所说,使用hibernate后支撑的长连接飞涨。2.    40w时系统出现异常。查看系统日志发现socket......
  • CF1818D 题解
    一、题目描述:给你一颗$n$个点,$m$条边的简单无向图,可能不连通。我们定义$鱼图$为满足以下条件的无向图:$包含恰好\1\个环,环上有\1\个特殊的结点\u\,u\除了连在环上的\2\条边外还正好有\2\条边连向不在此环上的结点。$求是否存$鱼图$。若存......
  • 题解
    原题请见题目链接1.暴力求解这是我们线下水题赛的T1这里为初学者提供一个思路,假设我们现在刚入OI,在考场上如何怎么优雅的避免保灵显然我们只要用一个\(O(n)\)的暴力去扫描就行了,但是直接向后扫\(i+1,i+2\)这样走很容易寄。因为你在\(i+1\)跳的时候很容易把后面跳过了......
  • 【题解】[ABC304F] Shift Table(容斥)
    【题解】[ABC304F]ShiftTable题目链接ABC304F题意概述Takahashi和Aoki将在接下来的\(N\)天里兼职工作。Takahashi这\(n\)天的出勤情况由字符串\(S\)表示,其中\(S\)的第\(i\)个字符是#则表示他在第\(i\)天工作,第\(i\)个字符是.表示他在第\(i\)天休息......
  • 题解:【AGC054D】 (ox)
    题目链接Larry76牛牛首先考虑没有ox怎么做,就是将括号序列调成合法。\(|S|\)不大直接模拟一遍,记录\(now\)表示一个前缀权值,当遇到一个(时\(+1\),遇到一个)时\(-1\),当\(now<0\)的时候说明序列不合法即)多了,暴力向后找到第一个(交换到当前的)前面。这样我们......
  • Codeforces Round 876 (Div. 2)题解
    CodeforcesRound876(Div.2)A.TheGoodArray标签greedymath题意对于任意\(i\in\{1,2,\dots,n\}\),要求数列\(a\)满足前\(i\)个元素中至少有\(\lceil\frac{i}{k}\rceil\)个元素为\(1\),后\(i\)个元素中至少有\(\lceil\frac{i}{k}\rceil\)个元素为\(1\)。思......