20200307 NOI Online T3 最小环(贪心)(数学问题)

T3 [NOI Online 提高组]最小环

[NOI Online 提高组]最小环


题目描述

给定一个长度为 n 的正整数序列 a i a_i ai,下标从 1 开始编号。我们将该序列视为一个首尾相邻的环,更具体地,对于下标为 i , j ( i ⩽ j ) i, j(i \leqslant j) i,j(ij) 的两个数 a i , a j a_i, a_j ai,aj*,它们的距离为 min ⁡ ( j − i , i + n − j ) \min(j-i, i+n-j) min(ji,i+nj)

现在再给定 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 1mn2×1050kn/21ai105

测试点编号 n ⩽ n \leqslant n特殊性质
110
218
336n 为偶数且 m = 1 , k = 2 m=1,k=2 m=1k=2
4,51000 m ⩽ 10 , k = 1 m \leqslant 10,k=1 m10k=1
650 m ⩽ 10 , k ⩽ 2 m \leqslant 10,k \leqslant 2 m10k2
7,83000
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值