CF1630D Flipping Range
题目大意
这道题的核心任务是对给定的数组 \(a\) 执行若干次操作,每次操作中,可以选择一个长度为 \(b_i\) 的子数组并将其所有元素的值取反。目标是最大化操作后数组元素之和。
题意分析
不难发现,其实我们所选择的区间肯定时越小越好的,因为这样子的话操作空间更大。于是通过类似于 gcd 的更相减损术,我们的最小长度就可以是所有 \(b_i\) 的最大公约数。具体参见 oi-wiki。设其最大公约数是 \(X\),于是对于每一对距离为 \(X\) 的点,可以通过两个相邻的区间实现取反。于是我们可以对于对 \(X\) 取模位置相同的所有数实现取反。接着对于奇偶不同的情况取最小值就行了。
CODE
#include<bits/stdc++.h>
#define wk(x) write(x),putchar(' ')
#define wh(x) write(x),putchar('\n')
#define ll long long
#define ull unsigned long long
#define ri register int
#define INF 2147483647
#define mod 998244353
#define N 1000005
#define NO printf("No\n")
#define YES printf("Yes\n")
using namespace std;
ll n,m,k,jk,ans,sum,num,cnt,tot;
ll dis[N],vis[N];
vector<int> wis[N];
void read(ll &x)
{
x=0;int ff=1;char ty;
ty=getchar();
while(!(ty>='0'&&ty<='9'))
{
if(ty=='-') ff=-1;
ty=getchar();
}
while(ty>='0'&&ty<='9')
x=(x<<3)+(x<<1)+ty-'0',ty=getchar();
x*=ff;return;
}
void write(ll x)
{
if(x==0)
{
putchar('0');
return;
}
if(x<0)
{
x=-x;
putchar('-');
}
char asd[201];int ip=0;
while(x) asd[++ip]=x%10+'0',x/=10;
for(int i=ip;i>=1;i--) putchar(asd[i]);
return;
}
signed main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(k);while(k--){
read(n),read(m);jk=cnt=tot=ans=0;
for(int i=1;i<=n;i++) read(dis[i]),ans+=abs(dis[i]);
for(int i=1;i<=m;i++) read(vis[i]),jk=(jk==0?vis[i]:__gcd(jk,vis[i]));
for(int i=1;i<=n;i++) wis[i%jk].push_back(dis[i]);
for(int i=0;i<jk;i++){sum=0;num=2e18;
for(ll j:wis[i]){
if(j<0) sum++;
num=min(num,abs(j));
}
if(sum%2==0) cnt+=num;
else tot+=num;
wis[i].resize(0);//记得初始化。
}wh(ans-2*min(cnt,tot));//输出最优答案。
}
return 0;
}
本文来自博客园,作者:Red_river_hzh,转载请注明原文链接:https://www.cnblogs.com/Red-river-hzh/p/19257053

浙公网安备 33010602011771号