CF 1182F Maximum Sine——根号算法
题目:http://codeforces.com/contest/1182/problem/F
注意有绝对值。
那么就是 k*p 对 q 取模,找最接近 \(\frac{q}{2}\) 的结果。
也就是 2*k*p 对 2*q 取模,找最接近 q 的结果。
一个二元组,第一维表示 %2q 后与 q 的距离,第二维表示自己的 编号。
对 n 个二元组排序太花时间。
令 \( g(x) = 2*p*x mod (2*q) \)
把 n 分块,只排序第一个块里的元素。因为 \( g(x+y) = g(x) + g(y) ( mod (2*q) ) \) ,所以其他块里的大小关系也可以通过第一个块的情况得知。
具体来说,设块大小是 bs ,在第 i 块就是要找与 \( q - 2*p*i*bs \) 最接近的元素。
用 lower_bound 的话,为了方便可以使 a[ tot+1 ] = a[ 1 ] , a[ 0 ] = a[ tot ] 。但是不能 a[ 0 ].first = a[ 1 ].first - 2*p , a[ 0 ].second = a[ 1 ].second - 1 ,因为模 2*q 意义下访问 0 就是要访问 tot 而不是真的 0 位置。
一定要注意相同 fir 只能保留最小的 second ,并且别用 unique 因为该函数不能做到刚才那个要求!!!
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define mkp make_pair #define fi first #define se second #define ll long long using namespace std; const int N=4e4+5,INF=1e9+5; int a,b,p,q,p2,q2,bs,tot; pair<ll,int> c[N],ans; int main() { int T;scanf("%d",&T); while(T--) { scanf("%d%d%d%d",&a,&b,&p,&q); p2=2*p; q2=2*q; bs=sqrt(b-a+1); tot=0; for(int i=0;i<bs;i++) c[++tot]=mkp((ll)p2*(a+i)%q2,i); sort(c+1,c+tot+1); //tot=unique(c+1,c+tot+1)-c-1; tot=0; for(int i=1;i<=bs;i++) if(i==1||c[i].fi!=c[i-1].fi) c[++tot]=c[i]; c[0]=c[tot]; c[tot+1]=c[1];// /*c[0]=mkp((c[1].fi+q2-p2)%q2,c[1].se-1); c[tot+1]=mkp((c[tot].fi+p2)%q2,c[tot].se+1);*/ int tp=(ll)p2*bs%q2,pls=0,pl2=a;//=a ans=mkp(INF,INF); for(int i=0,d;i<bs;i++,pls=((ll)pls+tp)%q2,pl2+=bs) { d=lower_bound(c+1,c+tot+1,mkp(((ll)q+q2-pls)%q2,0))-c-1;//-1 ans=min(ans,mkp(abs((c[d].fi+pls)%q2-q),c[d].se+pl2)); ans=min(ans,mkp(abs((c[d+1].fi+pls)%q2-q),c[d+1].se+pl2)); } for(int i=a+bs*bs;i<=b;i++)//a+ ans=min(ans,mkp(abs((ll)p2*i%q2-q),i)); printf("%d\n",ans.se); } return 0; }