T3 [NOI Online 提高组]最小环
题目描述
给定一个长度为 n 的正整数序列 a i a_i ai,下标从 1 开始编号。我们将该序列视为一个首尾相邻的环,更具体地,对于下标为 i , j ( i ⩽ j ) i, j(i \leqslant j) i,j(i⩽j) 的两个数 a i , a j a_i, a_j ai,aj*,它们的距离为 min ( j − i , i + n − j ) \min(j-i, i+n-j) min(j−i,i+n−j)。
现在再给定 m* 个整数 k 1 , k 2 , . . . , k m k_1, k_2,..., k_m k1,k2,...,km*,对每个 $ k_i(i=1, 2,…, m)$,你需要将上面的序列 a i a_i ai 重新排列,使得环上任意两个距离为 k i k_i ki 的数字的乘积之和最大。
输入格式
第一行两个正整数 n, m,表示序列长度与询问数。
接下来一行 n 个正整数表示 a i a_i ai 。
接下来 m 行每行一个非负整数表示 k i k_i ki 。
输出格式
共 m 行,每行一个整数表示答案。
输入输出样例
输入#1
6 3
1 2 3 4 5 6
0
1
2
输出#1
91
82
85
输入#2
6 1
1 2 3 4 5 6
3
输出#2
88
说明/提示
样例一解释
- k i = 0 k_i=0 ki=0 时:答案为每个数平方的和。
- k i = 1 k_i=1 ki=1 时:一种最优方案: { 3 , 1 , 2 , 4 , 6 , 5 } \{3,1,2,4,6,5\} {3,1,2,4,6,5}。答案为 3 × 1 + 1 × 2 + 2 × 4 + 4 × 6 + 6 × 5 + 5 × 3 = 82 3 \times 1 + 1 \times 2 + 2 \times 4 + 4 \times 6 + 6 \times 5 + 5 \times 3 = 82 3×1+1×2+2×4+4×6+6×5+5×3=82 。
- k i = 2 k_i=2 ki=2 时:一种最优方案: { 3 , 6 , 1 , 4 , 2 , 5 } \{3,6,1,4,2,5\} {3,6,1,4,2,5}。答案为 3 × 1 + 1 × 2 + 2 × 3 + 6 × 4 + 4 × 5 + 5 × 6 = 85 3 \times 1 + 1 \times 2 + 2 \times 3 + 6 \times 4 + 4 \times 5 + 5 \times 6 = 85 3×1+1×2+2×3+6×4+4×5+5×6=85 。
样例二解释
- 附加说明:样例当 k = 3 k=3 k=3 时,一个合法的排列是 1 , 5 , 3 , 2 , 6 , 4 1,5,3,2,6,4 1,5,3,2,6,4 ,答案为 88 88 88。注意这里答案不是 44 44 44。
数据范围与提示
对于所有测试数据: 1 ⩽ m ⩽ n ⩽ 2 × 1 0 5 , 0 ⩽ k ⩽ ⌊ n / 2 ⌋ , 1 ⩽ a i ⩽ 1 0 5 1 \leqslant m \leqslant n \leqslant 2 \times 10^5,0 \leqslant k \leqslant \lfloor n/2\rfloor,1 \leqslant a_i \leqslant 10^5 1⩽m⩽n⩽2×105,0⩽k⩽⌊n/2⌋,1⩽ai⩽105。
测试点编号 | n ⩽ n \leqslant n⩽ | 特殊性质 |
---|---|---|
1 | 10 | 无 |
2 | 18 | 无 |
3 | 36 | n 为偶数且 m = 1 , k = 2 m=1,k=2 m=1,k=2 |
4,5 | 1000 | m ⩽ 10 , k = 1 m \leqslant 10,k=1 m⩽10,k=1 |
6 | 50 | m ⩽ 10 , k ⩽ 2 m \leqslant 10,k \leqslant 2 m⩽10,k⩽2 |
7,8 | 3000 | 无 |
9,10 | 2 × 1 0 5 2 \times 10^5 2×105 | 无 |
思路:
手玩发现贪心的将两个大数放在一起时最优
example:
序列为: { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 } \{1,2,3,4,5,6,7,8\} {1,2,3,4,5,6,7,8} , k = 1 k=1 k=1
则最优排列为: { 8 , 7 , 5 , 3 , 1 , 2 , 4 , 6 } \{8,7,5,3,1,2,4,6\} {8,7,5,3,1,2,4,6}
则对于一个 k = 1 k=1 k=1 的序列可以 O ( n ) O(n) O(n) 求最大值
而对于 k i k_i ki ,可以将序列等价的分为 g c d ( n , k i ) gcd(n,k_i) gcd(n,ki) 组,答案为所有组乘积求和的最大值
于是将序列从大到小排序 ,每组贪心的选取最大的若干个,组成一个新序列,求最大值,再将每组的值累加,就是 k i k_i ki 的答案,时间复杂度 O ( n ) O(n) O(n)
对于 k i k_i ki 和 k j k_j kj ,如果 g c d ( n , k i ) = g c d ( n , k j ) gcd(n,k_i)=gcd(n,k_j) gcd(n,ki)=gcd(n,kj) ,则它们分的组数相等,对应的答案相等 。
对于不同的 g c d ( n , k ) gcd(n,k) gcd(n,k) 最多有 n \sqrt {n} n 种,于是可以记忆化。
总的时间复杂度 O ( n n ) O(n\sqrt {n}) O(nn)
代码:
#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long
#define lch p<<1
#define rch p<<1|1
inline char ch(){
static char buf[1<<21],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;
}
inline int in{
int s=0,f=1;char x;
for(x=ch();x<'0'||x>'9';x=ch()) if(x=='-') f=-1;
for( ;x>='0'&&x<='9';x=ch()) s=(s<<1)+(s<<3)+(x&15);
return f==1?s:-s;
}
const int A=2e5+5;
int n,m;
int val[A];
int sum[A];
int num[A];
int rt[A];
inline int lowbit(int x){
return x&(-x);
}
inline void add(int w){
while(w<=n){
rt[w]++;
w+=lowbit(w);
}
return;
}
inline int find(int w){
int ans=0;
while(w>0){
ans+=rt[w];
w-=lowbit(w);
}
return ans;
}
int res[A];
inline void perpare(){
int all=0,k=0;
for(int i=1;i<=n;i++){
add(val[i]);
sum[i]=i-find(val[i]);
all+=sum[i];
num[sum[i]]++;
}
res[0]=all;
for(int i=1;i<=n;i++){
k+=num[i-1];
if(i!=1) k--;
res[i]=res[i-1]-((n-i+1)-k);
}
return;
}
struct Tree{
int l,r,val,tag;
}tree[4*A];
inline void pushdown(int p){
if(tree[p].tag){
tree[lch].tag+=tree[p].tag;
tree[rch].tag+=tree[p].tag;
tree[p].tag=0;
}
return;
}
inline void build(int p,int l,int r){
tree[p].l=l,tree[p].r=r;
if(l==r){
tree[p].val=res[l];
return;
}
int mid=(l+r)>>1;
build(lch,l,mid),build(rch,mid+1,r);
return;
}
inline void add(int p,int l,int r,int val){
if(tree[p].l>=l&&tree[p].r<=r){
tree[p].tag+=val;
return;
}
pushdown(p);
int mid=(tree[p].l+tree[p].r)>>1;
if(l<=mid) add(lch,l,r,val);
if(r>=mid+1) add(rch,l,r,val);
return;
}
inline int qurey(int p,int w){
if(tree[p].l==tree[p].r) return tree[p].val+tree[p].tag;
pushdown(p);
int mid=(tree[p].l+tree[p].r)>>1;
if(w<=mid) return qurey(lch,w);
else return qurey(rch,w);
}
inline void work(int w){
if(val[w]>val[w+1]){
sum[w+1]--;
swap(val[w],val[w+1]);
swap(sum[w],sum[w+1]);
add(1,0,sum[w],-1);
}
else{
sum[w]++;
swap(val[w],val[w+1]);
swap(sum[w],sum[w+1]);
add(1,0,sum[w+1]-1,1);
}
return;
}
signed main(){
// freopen("bubble.in","r",stdin);
// freopen("bubble.out","w",stdout);
n=in,m=in;
for(int i=1;i<=n;i++)
val[i]=in;
perpare();
build(1,0,n);
while(m--){
int opt=in,k=in;
if(opt==1) work(k);
else if(opt==2){
if(k>=n) printf("0\n");
else printf("%lld\n",qurey(1,k));
}
}
return 0;
}