JZOJ7月21日提高组T4 WTF交换
发布日期:2021-05-06 15:39:30 浏览次数:29 分类:精选文章

本文共 4556 字,大约阅读时间需要 15 分钟。

JZOJ7月21日提高组T4 WTF交换【Special Judge】

题目

Description

假定给出一个包含N个整数的数组A,包含N+1个整数的数组ID,与整数R。其中ID数组中的整数均在区间[1,N-1]中。

用下面的算法对A进行Warshall-Turing-Fourier变换(WTF):
sum = 0
for i = 1 to N
index = min{ ID[i], ID[i+1] }
sum = sum + A[index]
将数组A往右循环移动R位
将数组A内所有的数取相反数
for i = 1 to N
index = max{ ID[i], ID[i+1] }
index = index + 1
sum = sum + A[index]
将数组A往右循环移动R位
给出数组A以及整数R,但没有给出数组ID。在对数组A进行了WTF算法后,变量sum的可能出现的最大值数多少?

Input

第一行包含两个整数N与R。

第二行包含N个整数,代表A[1]到A[N]的值。

Output

第一行输出变量sum可能出现的最大值。

第二行输出此时的ID数组,包含N+1个整数。必须满足ID数组中每一个数均在区间[1,N-1]中。若有多个满足的ID数组,输出任意一组。
如果第一行是正确的(不管有没有输出第二行),你能得到该测试点50%的得分。

Sample Input

输入1:

5 3
1 -1 1 -1 1
输入2:
6 5
2 5 4 1 3 5

Sample Output

输出1:

10
1 1 1 2 2 3
输出2:
16
3 2 1 1 5 4 1

Data Constraint

对于20%的数据,N<=7。

对于60%的数据,N<=300。
对于100%的数据,2<=N<=3000, 1<=R<N, -10000<=A[i]<=10000。

题解

题意

有一种叫做WTF交换的东东(具体见题面)

现在给出数组 A A A R R R N N N,求最大的 s u m sum sum和满足最大 s u m sum sum I D ID ID数组(任意一种即可)

分析

DP

f [ i ] [ j ] f[i][j] f[i][j]表示 I D ID ID数组的第 i i i位选择 j j j的最大 s u m sum sum

第一行

最初转移

枚举一个 k ( 1 k(1 k(1~ n − 1 ) n-1) n1)

f [ i ] [ j ] = m a x ( f [ i − 1 ] [ k ] + a [ g e t ( m i n ( j , k ) ) ] − a [ g e t ( m a x ( j , k ) + 1 ) ] ) f[i][j]=max(f[i-1][k]+a[get(min(j,k))]-a[get(max(j,k)+1)]) f[i][j]=max(f[i1][k]+a[get(min(j,k))]a[get(max(j,k)+1)])
何为 g e t get get
若当前选择第 i i i个位置
那么之前肯定进行过 i − 1 i-1 i1次WTF操作
那么这个 j ( k ) j(k) j(k)肯定会在数组内调换位置
g e t get get操作就是把一开始 j ( k ) j(k) j(k)的位置找到
显而易见,这个转移是 O ( n 3 ) O(n^3) O(n3),只能拿60%的数据

优化

分类讨论

k ≤ j k≤j kj时:
原方程变为: f [ i ] [ j ] = m a x ( f [ i − 1 ] [ k ] + a [ g e t ( k ) ] − a [ g e t ( j + 1 ) ] ) f[i][j]=max(f[i-1][k]+a[get(k)]-a[get(j+1)]) f[i][j]=max(f[i1][k]+a[get(k)]a[get(j+1)])
可以看出, f [ i − 1 ] [ k ] + a [ g e t ( k ) ] f[i-1][k]+a[get(k)] f[i1][k]+a[get(k)]是和 j j j没有关系的
既然要让答案最大,那么 f [ i − 1 ] [ k ] + a [ g e t ( k ) ] f[i-1][k]+a[get(k)] f[i1][k]+a[get(k)]就应该最大
那么就可以设一个 s s s表示最大的 f [ i − 1 ] [ k ] + a [ g e t ( k ) ] f[i-1][k]+a[get(k)] f[i1][k]+a[get(k)]
至于怎么满足 k ≤ j k≤j kj,就可以让 j j j顺着扫一遍,那么最大的 s s s所对应的 k k k肯定是在 1 1 1~ j j j里的
手动模拟过程:

  1. 新的 j j j进入
  2. 判断 f [ i − 1 ] [ j ] + a [ g e t ( j ) ] f[i-1][j]+a[get(j)] f[i1][j]+a[get(j)] s s s的大小,更新 s s s
  3. 判断 s − a [ g e t ( j + 1 ) ] s-a[get(j+1)] sa[get(j+1)] f [ i ] [ j ] f[i][j] f[i][j]的大小,更新 f [ i ] [ j ] f[i][j] f[i][j]

j ≤ k j≤k jk时:

原方程变为: f [ i ] [ j ] = m a x ( f [ i − 1 ] [ k ] + a [ g e t ( j ) ] − a [ g e t ( k + 1 ) ] ) f[i][j]=max(f[i-1][k]+a[get(j)]-a[get(k+1)]) f[i][j]=max(f[i1][k]+a[get(j)]a[get(k+1)])
可以看出, f [ i − 1 ] [ k ] − a [ g e t ( k + 1 ) ] f[i-1][k]-a[get(k+1)] f[i1][k]a[get(k+1)]是和 j j j没有关系的
既然要让答案最大,那么 f [ i − 1 ] [ k ] − a [ g e t ( k + 1 ) ] f[i-1][k]-a[get(k+1)] f[i1][k]a[get(k+1)]就应该最大
那么就可以设一个 s s s表示最大的 f [ i − 1 ] [ k ] − a [ g e t ( k + 1 ) ] f[i-1][k]-a[get(k+1)] f[i1][k]a[get(k+1)]
至于怎么满足 j ≤ k j≤k jk,就可以让 j j j倒着扫一遍,那么最大的 s s s所对应的 k k k肯定是在 j j j~ n − 1 n-1 n1里的
手动模拟过程:

  1. 新的 j j j进入
  2. 判断 f [ i − 1 ] [ j ] − a [ g e t ( j + 1 ) ] f[i-1][j]-a[get(j+1)] f[i1][j]a[get(j+1)] s s s的大小,更新 s s s
  3. 判断 s + a [ g e t ( j ) ] s+a[get(j)] s+a[get(j)] f [ i ] [ j ] f[i][j] f[i][j]的大小,更新 f [ i ] [ j ] f[i][j] f[i][j]

原谅我偷懒

那么最大的 s u m sum sum就是最大的 f [ n ] [ i ] f[n][i] f[n][i]
这时第一行求处理完了

第二行

处理第二行,其实就是要记每个 f [ i ] [ j ] f[i][j] f[i][j]是由哪个 k k k转移过来的

开一个新的数组 g [ i ] [ j ] g[i][j] g[i][j]表示 I D ID ID数组的第 i i i位选择 j j j的最大 s u m sum sum是从哪个 k k k转移过来的
在上述的更新 f [ i ] [ j ] f[i][j] f[i][j]的那一步里顺便更新 g [ i ] [ j ] g[i][j] g[i][j]
注意输出的时候,因为我们是由 g [ 1 ] [ ] g[1][] g[1][]转移到 g [ 2 ] [ ] g[2][] g[2][]转移到 g [ 3 ] [ ] g[3][] g[3][]……最后转移到 g [ n ] [ ] g[n][] g[n][],所以要 d g dg dg输出
先别急着去打
发现,这里的 g [ i ] [ j ] g[i][j] g[i][j]只有 n n n个数
但题目要求的是 n + 1 n+1 n+1个数
那么第 n + 1 n+1 n+1个数是什么???
其实就是最大 f [ n ] [ i ] f[n][i] f[n][i] i i i

Code

#include
#include
#define inf 1234567890#define get(x) ((((x)-(i-1)*r)%n+n-1)%n+1)using namespace std;int n,r,i,j,s,ss,t,ans,a[3005],f[3005][3005],g[3005][3005];void dg(int x,int before){ if (x!=0) dg(x-1,g[x][before]); printf("%d ",before);}int main(){ scanf("%d%d",&n,&r); for (i=1;i<=n;i++) scanf("%d",&a[i]); for (i=1;i<=n;i++) { ss=-inf; for (j=1;j
ss) { ss=s; t=j; } if (ss-a[get(j+1)]>f[i][j]) { f[i][j]=ss-a[get(j+1)]; g[i][j]=t; } } ss=-inf; for (j=n-1;j>=1;j--) { s=f[i-1][j]-a[get(j+1)]; if (s>ss) { ss=s; t=j; } if (ss+a[get(j)]>f[i][j]) { f[i][j]=ss+a[get(j)]; g[i][j]=t; } } } ans=-inf; for (i=1;i
ans) { ans=f[n][i]; t=i; } printf("%d\n",ans); dg(n,t); return 0;}
上一篇:JZOJ7月21日提高组反思
下一篇:蒟蒻的费马小定理求逆元

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2025年03月27日 19时19分13秒