link
dp, 2400,非常妙的一道题目
类似这种每次操作都是对连续
k
k
k 位进行的不妨用对位置取模的dp
题意
给定数组
a
a
a 和集合
B
B
B,你可以进行如下操作任意多次(可以为0次):
∀
x
∈
B
,
翻
转
数
组
a
中
连
续
的
x
位
,
同
一
个
x
也
可
以
多
次
取
出
\forall x\in B,翻转数组a中连续的x位,同一个x也可以多次取出
∀x∈B,翻转数组a中连续的x位,同一个x也可以多次取出
问:
m
a
x
Σ
a
i
max\space \Sigma a_i
max Σai
思路
首先假设
x
,
y
∈
B
(
x
>
y
)
x,y\in B(x > y)
x,y∈B(x>y),则我们可以把任意一个长度为x-y的区间翻转,容易得到翻转的最小长度即为g=gcd(b1,b2,...bn)
,所以问题简化为了:
给定
a
,
g
a,g
a,g,每次操作可以翻转数组
a
a
a 中连续的
g
g
g 位,求
m
a
x
Σ
a
max\space\Sigma a
max Σa
构造一个数列
c
c
c ,若最后
a
i
a_i
ai 乘了-1,则
c
i
=
1
c_i=1
ci=1,否则
c
i
=
0
。
c_i=0。
ci=0。
设
f
x
=
⨁
i
%
g
c
i
,
x
∈
[
0
,
g
−
1
]
f_x=\bigoplus_{i\%g}c_i,x\in [0,g-1]
fx=⨁i%gci,x∈[0,g−1],由于是翻转连续的g位,所以每次操作相当于
∀
x
∈
[
0
,
g
−
1
]
,
f
x
=
f
x
⨁
1
\forall x\in[0,g-1],f_x = f_x\bigoplus1
∀x∈[0,g−1],fx=fx⨁1也就是无论怎样变换,所有的
f
x
f_x
fx 都是相等的,同为1or同为0。
另一方面,我们也可以通过变换得到任意一个
f
x
f_x
fx 相等的局面,所以f_x均为1或0是一个序列合法的等价条件
以此进行dp。
设dp[i][1/0]
表示当前进行到第
i
i
i 位,且进行了1/0(奇/偶次)变换后得到的
a
i
+
a
i
−
g
+
.
.
.
a_i+a_{i-g}+...
ai+ai−g+...最大值。最后的答案就是1/0
中大的那个。
int n, m;
int gcd(int a, int b) {
if(!b) return a;
return gcd(b, a % b);
}
int a[maxn];
ll dp[maxn][2];//dp[i][0]表示(i+1)%g位置共操作偶数次 [i][1]表示共操作奇数次
void solve() {
cin >> n >> m;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
int g = 0;
while(m--) {
int x;
cin >> x;
g = gcd(g, x);
}
// cout << g << endl;
for(int i = 1; i <= g; i++) {
dp[i][1] = -a[i];
dp[i][0] = a[i];
}
for(int i = g + 1; i <= n; i++) {
dp[i][1] = max(dp[i-g][0] - a[i], dp[i-g][1] + a[i]);
dp[i][0] = max(dp[i-g][1] - a[i], dp[i-g][0] + a[i]);
}
ll sum1 = 0, sum0 = 0;
for(int i = n-g+1; i <= n; i++) {
sum1 += dp[i][1];
sum0 += dp[i][0];
}
cout << max(sum1, sum0) << endl;
}