Fork me on GitHub

小奇的数列

【题目 背景】

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

【问题描述】

给定一个长度为 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('\n')
 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 }

 

posted @ 2018-10-29 17:16  H_LAUV  阅读(623)  评论(0编辑  收藏  举报
Live2D //博客园自带,可加可不加