【题解】P8663 题解

P8663 题解

思路分析

余数分析题。

设满足条件的三数为 a,b,ca,b,c,由题意得 a+b+c0(modk)a + b + c \equiv 0 \pmod k

于是有 amodk+bmodk+cmodk0(modk)a\bmod k + b \bmod k + c \bmod k \equiv 0 \pmod k

又根据余数的性质,amodk,bmodk,cmodk<ka \bmod k,b \bmod k,c \bmod k < k,即 amodk+bmodk+cmodk<3ka \bmod k + b \bmod k + c \bmod k < 3k

所以,amodk+bmodk+cmodk=0a \bmod k + b \bmod k + c \bmod k = 0amodk+bmodk+cmodk=ka \bmod k + b \bmod k + c \bmod k = kamodk+bmodk+cmodk=2ka \bmod k + b \bmod k + c \bmod k = 2k

于是,我们枚举 amodka \bmod kbmodkb \bmod kamodk+bmodk+cmodka \bmod k + b \bmod k + c \bmod k 即可,这样便可以 O(k2)O(k^2) 的复杂度遍历 amodka \bmod kbmodkb \bmod kcmodkc \bmod k

此时问题转化为:已知 amodka \bmod kbmodkb \bmod kcmodkc \bmod k,求 a+b+ca+b+c 的最大值。

这个其实直接更新就可以了,记原数组为 pp,具体步骤如下:

  • 开一个数组 fffif_i 代表在 pp 中,模 kkii 的数的最大值。
  • fpimodk=max(pi,fpimodk)f_{p_i \bmod k}=\max(p_i,f_{p_i \bmod k})

但是需要注意一个问题:数不能重复选。当 amodka \bmod kbmodkb \bmod k 相同时,bmodkb \bmod k 应选择次大的,以此类推。当 amodka \bmod kbmodkb \bmod kcmodkc \bmod k 相同时,bmodkb \bmod kcmodkc \bmod k 分别应选择次大和次次大。

于是,ff 数组应该改成三维的,三维分别表示:

  • fi,0f_{i,0} 代表在 pp 中,模 kkii 的数的最大值。
  • fi,1f_{i,1} 代表在 pp 中,模 kkii 的数的次大值。
  • fi,2f_{i,2} 代表在 pp 中,模 kkii 的数的次次大值。

为什么不从 11 开始呢?因为这样只要通过 f[j][(i==j)] 就可以算出对应的数,其他同理。

也就是 res = max(res, f[i][0]+f[j][(i==j)]+f[g][(i==g)+(j==g)])

最后再附上同时计算最大值、次大值、次次大值的算法。

设最大值、次大值、次次大值分别为 xxyyzz,当前输入的数为 tt

  • x<tx < t,则 z=yz = yy=xy=xx=tx=t
  • 否则,若 y<ty < t,则 z=yz = yx=yx=y
  • 再否则,若 z<tz < t,则 z=tz = t

代码

#include <iostream>
#include <iomanip>
#include <cmath>
#include <string>
#include <algorithm>
#include <cstdio>
#include <cstring>
#define endl '\n'
#define int long long
#define IL inline
using namespace std;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;

IL int read()
{
    int x = 0,f = 1;
    char c = getchar();
    while(c <'0'|| c >'9'){if(c == '-') f = -1;c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar();
    return x * f;
}

void write(int x)
{
    if(x < 0) putchar('-'),x = -x;
    if(x > 9) write(x / 10);
    putchar(x % 10 + '0');
}

int f[N][3];
int a[N];
int res = -1e14;

signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int n, k;
	cin >> n >> k;
	for(int i = 0;i < k;i++) f[i][0] = f[i][1] = f[i][2] = -6e8; //初始化
	for(int i = 1;i <= n;i++)
	{
		cin >> a[i];
		int t = a[i];
		int u = a[i] % k;
		if(f[u][0] < t) //统计最大、次大、次次大
		{
			f[u][2] = f[u][1];
			f[u][1] = f[u][0];
			f[u][0] = t;
		}
		else if(f[u][1] < t)
		{
			f[u][2] = f[u][1];
			f[u][1] = t;
		}
		else if(f[u][2] < t)
		{
			f[u][2] = t;
		}
	}
	for(int i = 0;i < k;i++)
	{
		for(int j = 0;j < k;j++)
		{
			for(int z = 0;z <= 2 * k;z += k) //枚举每个余数
			{
				int g = z - i - j;
				if(g < 0 || g >= k) continue; //越界跳过
				res = max(res, f[i][0] + f[j][(i == j)] + f[g][(i == g) + (j == g)]); //统计答案
			}
		}
	}
	cout << res << endl;
    return 0;
}
posted @ 2023-08-10 17:22  邻补角-SSA  阅读(12)  评论(0)    收藏  举报  来源