F. Pairwise Modulo——(Harbour.Space Scholarship Contest 2021-2022 (open for everyone, rated, Div. 1 + Div. 2))
F. Pairwise Modulo
题意
给出一个长度为\(n\)的排列\(a\),定义
要求输出\(p_1, \cdots, p_n\)。
思路
这里暴力取模运算明显会\(T\),因此将取模运算化为:\(a_i\ mod\ a_j = a_i - \lfloor \frac{a_i} {a_j} \rfloor a_j\),并且将\(i\)和\(j\)确定顺序,这样\(p\)序列就有了递推的性质,即
另\(f_k = \sum_{1 \leq j < i \le k} a_i - \lfloor \frac{a_i} {a_j} \rfloor a_j, g_k = \sum_{1 \le i < j \le k}a_i - \lfloor \frac{a_i} {a_j} \rfloor a_j\)。
对于\(f_k\),\(f_k = f_{k-1} + \sum_{1 \le j < k} a_k - \lfloor \frac{a_k} {a_j} \rfloor a_j = f_{k-1} + a[k] * (k - 1) - \sum_{1\le j<k}\lfloor \frac{a_k} {a_j} \rfloor a_j\),
对于\(\sum_{1\le j<k}\lfloor \frac{a_k} {a_j} \rfloor a_j\)这一项,我们可以使用树状数组维护,
当\(a_k \in [a_j,2*a_j-1]\),贡献为\(a_j\),
当\(a_k \in [2*a_j, 3*a_j-1]\),贡献为\(2*a_j\)
\(\cdots\)
所以我们使用树状数组进行区间维护差分即可,对于区间\([ia_k,(i+1)a_k-1]\),在\(ia_k\)处增加\(i*a_k\),在\((i+1)*a_k\)处减去\(i*a_k\)。对于第\(k\)项直接\(query(a_k)\)求前缀和即可。
对于\(g_k = \sum_{1 \le i < j \le k}a_i - \lfloor \frac{a_i} {a_j} \rfloor a_j = f_{k-1} + \sum_{1 \le i < k}a_i - \lfloor \frac{a_i} {a_k} \rfloor a_k = f_{k-1} + \sum_{1 \le i < k}a_i - \sum_{1 \le i < k}\lfloor \frac{a_i} {a_k} \rfloor a_k\),
\(\sum_{1 \le i < k}a_i\) 直接累计计算即可,而后面的一项则需要使用树状数组维护,因为:
对于\(a_i \in [a_k,2*a_k-1]\),\(\lfloor \frac{a_i} {a_k} \rfloor\)的值为\(1\),
对于\(a_i \in [2*a_k,3*a_k-1]\),\(\lfloor \frac{a_i} {a_k} \rfloor\)的值为\(2\),
\(\cdots\)
可以对这个排列建立树状数组维护其有没有出现(\(1或者0\)),然后遍历\(a[k]\)的倍数区间查询即可得到答案,这个复杂度为\(logn\),可以接受。
Code
#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
#define V vector
using namespace std;
const int N = 3e5 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int a[N];
struct Tree{
ll c[N];
void add(int x,ll y){
for(;x < N; x += x&-x){
c[x] += y;
}
}
ll query(int x){
if(x < 0)
return 0ll;
ll ans = 0;
for(;x > 0;x -= x&-x)
ans += c[x];
return ans;
}
ll ask(int x,int y){
return query(y) - query(x - 1);
}
}A,B;
void solve(){
int n;
scanf("%d",&n);
ll sum = 0,ans = 0;
for(int i = 1; i <= n; i++){
scanf("%d",&a[i]);
sum += a[i];
ans += (ll)a[i] * (i - 1);
ans += sum - a[i];
ans -= A.query(a[i]);
for(int j = a[i]; j < N; j += a[i]){
int l = j, r = min(N - 2, l + a[i] - 1);
ans -= B.ask(l, r) * j;
A.add(l, l);
A.add(r + 1, -l);
}
B.add(a[i],1);
printf("%lld ",ans);
}
printf("\n");
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int T = 1;
// scanf("%d",&T);
while(T--){
solve();
}
return 0;
}