516. 求和
【问题描述】
给出一个数列A1,A2….,An和K,P。
设Sij=Ai+Ai+1+…+Aj
Anaswer=min{Si,j mod P | si,j mod P≥K),其中i≤j,(si,j mod P | si,j mod P≥K}非空。
【输入格式】
第一行一个正整数n,K,P。
第二行n个整数,表示一个数列A1,A2,…,An
【输出格式】
在第一行输出Answer。
【输入样例】
7 2 17
12
13
15
11
16
26
11
【输出样例】
2
【数据范围】
在100%的数据中,1<n<100000,1<K,P,ai<10^8,i=1,2…n
treap的题目
求出前缀和,一边求一边取膜,每一次插入一个前缀和x,插入之前去查找一个值y,使(x-y)%p>=k,我们分两种情况来讨论,x>=y和x<y,当x>=y时x-y是一个正数,我们直接在平衡树
中查找x-k的前驱,x-y即为所求值,取min;对于x<y,因为a全是正数,所以x取膜前一定比y取膜前大,那么我们可以对x+p,再-y,得到的即为当y>x时这一段取膜后的值,同样查找x+p-k的前驱。如果x+p-k查到的y<=x怎么办?其实不用管,因为他肯定不会比x-k查完之后求得的值更优。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int inf=100005; 4 struct treap{ 5 int rnd; 6 int l,r; 7 int v; 8 int siz; 9 int w; 10 }t[inf]; 11 void update(int x){ 12 t[x].siz=t[t[x].l].siz+t[t[x].r].siz+t[x].w; 13 } 14 void lrot(int &x){ 15 int k=t[x].r; 16 t[x].r=t[k].l; 17 t[k].l=x; 18 update(x); 19 update(k); 20 x=k; 21 } 22 void rrot(int &x){ 23 int k=t[x].l; 24 t[x].l=t[k].r; 25 t[k].r=x; 26 update(x); 27 update(k); 28 x=k; 29 } 30 int rt,sz; 31 int a[inf],n,k,p,mi=0x7fffffff; 32 int Rand(){ 33 return ((rand()<<12)+rand())%20021031; 34 } 35 void insert(int &x,int v){ 36 if(!x){ 37 x=++sz; 38 t[x].rnd=Rand(); 39 t[x].v=v; 40 t[x].siz=t[x].w=1; 41 return ; 42 } 43 t[x].siz++; 44 if(t[x].v==v){ 45 t[x].w++; 46 return ; 47 } 48 if(v<t[x].v){ 49 insert(t[x].l,v); 50 if(t[t[x].l].rnd<t[x].rnd)rrot(x); 51 } 52 else { 53 insert(t[x].r,v); 54 if(t[t[x].r].rnd<t[x].rnd)lrot(x); 55 } 56 } 57 int pre(int x,int v,int Max){ 58 if(!x)return Max; 59 if(t[x].v>v)return pre(t[x].l,v,Max); 60 else return pre(t[x].r,v,max(Max,t[x].v)); 61 } 62 int main() 63 { 64 scanf("%d%d%d",&n,&k,&p); 65 insert(rt,0); 66 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 67 for(int i=1;i<=n;i++){ 68 a[i]+=a[i-1]; 69 a[i]%=p; 70 int tem1=pre(rt,a[i]-k,-0x3fffffff); 71 int tem2=pre(rt,a[i]+p-k,-0x3fffffff); 72 mi=min(mi,min(a[i]-tem1,a[i]+p-tem2)); 73 insert(rt,a[i]); 74 } 75 printf("%d\n",mi); 76 return 0; 77 }