zoukankan      html  css  js  c++  java
  • 小奇的数列

    【题目 背景】

    小奇总是在数学课上思考奇怪的问题。

    【问题描述】

    给定一个长度为 n 的数列, 以及 m 次询问, 每次给出三个数 l, r 和 P, 询问 (a[l' ] + a[l' +1] + . . . + a[r' ] ) mod P 的最小值。 其中 l <= l' <= r' <= r。 即模意义下的区间子串和最小值。

    【输入格式】

    第一行包含两个正整数 n 和 m, 表示数列的长度和询问的个数。 第二行为 n 个整数, 为 a[1] . . a[n] 。 接下来 m 行, 每行三个数 l, r 和 P, 代表一次询问。

    【输出格式】

    对于每次询问, 输出一行一个整数表示要求的结果

    【样例输入】

    2

    15 9 9

    1 3 10

    1 4 17

    【样例输出】

    21

    【数据范围】

    对于 20%的数据 n<=100, m<=100, p<=200

    对于 40%的数据 n<=200, m<=1000, p<=500 

    对于 70%的数据 n<=100000, m<=10000, p<=200

    对于 100%的数据 n<=500000, m<=10000, p<=500, 1<=a[i] <=10^9


     题解(这是老师发下来的)

    对于20%的数据,最暴力的做法,在区间内枚举子串,暴力求和取模,最后得出最小值,复杂度m*n^3

    对于40%的数据,算法1的区间求和改用前缀和相减,复杂度m*n^2

    接下来,思考一下抽屉原理,如果询问的区间长度大等p的话,一定有两个前缀和相减等于0,那么直接输出0即可

    对于70%的数据,把所有长度大等p的区间特判,其余暴力,复杂度m*p^2

    考虑生日悖论,数列如果是随机生成的,找到2个前缀和相同的就返回0,期望复杂度为m*p

    由于第10组数据是随机生成的,所以算法3可以通过8个测试点

    对于100%的数据,使用平衡树来寻找某个数的前驱,复杂度为m*p*logp(可能高于noip难度),需要手写treap,splay或替罪羊树等。set由于常数较大,实测和暴力结果一样。

    (下面是我自己的话)

    首先下面这一句话十分有用:

    接下来,思考一下抽屉原理,如果询问的区间长度大等p的话,一定有两个前缀和相减等于0,那么直接输出0即可

    但如果你没法理解,可以自己举几个例子直观感知一下(小提示,往余数上想想)

    所以对于每一个询问区间我们都 Insert  出一个平衡树 就是把之前记录的前缀和 从 s[l]-s[l-1] ~ s[r]-s[l-1],每一段前缀和都插入,然后边插入一遍查询此时这一段前缀和在平衡树里的前继是谁,此时的前继与这一段前缀和的差值一定是最小的。对于每一段都这样查询,就可以得到这个区间在模P意义下的最小字段和了。

    具体细节请看代码

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstdlib>
      4 #include<cstring>
      5 #include<algorithm>
      6 
      7 #define For(i,a,b) for(register int i=a;i<=b;++i)
      8 #define Dwn(i,a,b) for(register int i=a;i>=b;--i)
      9 #define Re register
     10 #define Pn putchar('
    ')
     11 #define llg long long
     12 using namespace std;
     13 const int N=5e5+10;
     14 int n,m,p;
     15 int ch[N][2],val[N],cnt[N],fa[N],BH=0;
     16 int a[N],root=0,l,r,fn;
     17 llg s[N];
     18 inline void read(int &v){
     19     v=0;
     20     char c=getchar();
     21     while(c<'0'||c>'9')c=getchar();
     22     while(c>='0'&&c<='9')v=v*10+c-'0',c=getchar();
     23 }
     24
     25 void write(int x){
     26     if(x>9)write(x/10);
     27     int xx=x%10;
     28     putchar(xx+'0');
     29 }
     30 void clear(int x){
     31     ch[x][0]=ch[x][1]=fa[x]=cnt[x]=val[x]=0;
     32 }
     33 bool Wson(int x){
     34     return ch[fa[x]][1]==x;
     35 }
     36 void rotate(int x){
     37     int y=fa[x];
     38     int z=fa[y];
     39     int ws=Wson(x);
     40     
     41     if(z)ch[z][Wson(y)]=x;
     42     fa[x]=z;
     43     
     44     ch[y][ws]=ch[x][ws^1];
     45     fa[ch[y][ws]]=y;
     46     
     47     ch[x][ws^1]=y;
     48     fa[y]=x;
     49 }
     50 
     51 void Splay(int x,int goal){
     52     while(fa[x]!=goal){
     53         int y=fa[x];
     54         if(fa[y]!=goal){
     55             if(Wson(x)==Wson(y))rotate(y);
     56             else rotate(x);
     57         }
     58         rotate(x);
     59     }
     60     if(goal==0)root=x;
     61 }
     62 
     63 void Insert(int dt){
     64     if(root==0){
     65         int now=++BH;
     66         root=now;
     67         ch[now][0]=ch[now][1]=0;
     68         val[now]=dt; fa[now]=0; cnt[now]=1;
     69         return;
     70     }
     71     int now=root,pa=0;
     72     while(1){
     73         if(val[now]==dt){
     74             cnt[now]++; Splay(now,0); return;
     75         }
     76         pa=now;
     77         now=ch[now][val[now]<dt];
     78         if(!now){
     79             now=++BH;
     80             ch[now][1]=ch[now][0]=0;
     81             val[now]=dt; fa[now]=pa; cnt[now]=1;
     82             ch[pa][val[pa]<dt]=now;
     83             Splay(now,0); return;
     84         }
     85     }
     86 }
     87 
     88 int Pre(){
     89     int now=root;
     90     if(cnt[now]>1)return val[now];
     91     now=ch[now][0];
     92     while(ch[now][1])now=ch[now][1];
     93     return val[now];
     94 }
     95 
     96 int main(){
     97 //    freopen("seq.in","r",stdin);
     98 //    freopen("seq.out","w",stdout);
     99     read(n); read(m);
    100     For(i,1,n) read(a[i]);
    101     For(i,1,n)s[i]=s[i-1]+a[i];
    102     For(i,1,m){
    103         read(l); read(r); read(p);
    104         root=BH=0; fn=2147483600;
    105         Insert(0);
    106         if(r-l+1>=p){
    107             putchar('0'); Pn;
    108             continue;
    109         }
    110         For(j,l,r){
    111             int tmp=(s[j]-s[l-1])%p;
    112             Insert(tmp);
    113             int px=Pre();
    114             fn=min(fn,tmp-px);
    115             if(fn==0)break;
    116         }
    117         write(fn); Pn;
    118     }
    119     fclose(stdin); fclose(stdout);
    120     return 0;
    121 }
  • 相关阅读:
    求全排列,调用C++函数
    ZOJ 3508 (the war)
    HDU 1285
    SDUT--枚举(删数问题)
    SDUT--进制转换
    位运算
    [NOI2015]软件包管理器
    列队[noip2017]
    [APIO2007]动物园
    [NOI2001]炮兵阵地
  • 原文地址:https://www.cnblogs.com/HLAUV/p/9871768.html
Copyright © 2011-2022 走看看