QZOI2016 兔警官朱迪跑圈
题面传送门
题面概述:给定\(s\)序列,满足对于所有\(1\leq i<n\),\(s_i\leq s_{i+1}\),求\(\sum\limits_{i=1}^{n}{\sum\limits_{j=1}^{i-1}{\lfloor\frac{L(s_i-s_j)}{s_n}\rfloor}}\)
然后你会发现一个很有趣的数据范围:\(s_i\leq 10^6\)
这就提示我们这道题和域值有关。
有一个很naive的想法就是设\(w_i=\lfloor\frac{L\times s_i}{s_n}\rfloor\),然后对于每个\(i\)求\(w_i\times (i-1)-\sum\limits_{j=1}^{i-1}{w_i}\)很可惜这个是错的。
然而我们发现就是整除的地方出了问题:这个方法没有办法满足整除。
这个东西的差值对于每对最大是\(1\)
设\(q_i=L\times s_i\bmod s_n\),如果对于一对\((i,j)\)满足\(q_i\leq q_j\)那么就会产生\(1\)的贡献。
那么可以用树状数组维护二维偏序就好了吧。
时间复杂度\(O(nlogn+nlogw)\)
code:
#include<cstdio>
#include<algorithm>
#define N 100039
#define W 1000039
#define ll long long
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,m,k,x,y,z,f[W];ll ans,a,b,cnt,maxn,tot,pus;
struct yyy{ll x,w;}s[N];
I bool cmp(yyy x,yyy y){return x.x<y.x;}
I void get(int x){while(x<=maxn) f[x]++,x+=x&-x;}
I int find(int x){int ans=0;while(x) ans+=f[x],x-=x&-x;return ans;}
int main(){
freopen("1.in","r",stdin);
register int i,j;scanf("%d%lld%lld",&n,&b,&a);
for(i=1;i<=n;i++) scanf("%d",&s[i].x),maxn=max(maxn,s[i].x);
for(i=1;i<=n;i++) s[i].w=s[i].x*b%maxn;sort(s+1,s+n+1,cmp);
for(i=1;i<=n;i++)tot=s[i].x*b/maxn,ans+=(tot-1)*(i-1)-pus+find(s[i].w+1),get(s[i].w+1),pus+=tot;
printf("%lld\n",ans);
}