A
不难发现是要找一个 x x x 使这个东西最小: ∑ i = 1 n a i m o d x + ⌊ a i x ⌋ \sum_{i=1}^n a_i \bmod x+\lfloor \dfrac {a_i} x \rfloor ∑i=1naimodx+⌊xai⌋。
推一推:
∑
i
=
1
n
a
i
−
x
×
⌊
a
i
x
⌋
+
⌊
a
i
x
⌋
=
(
∑
i
=
1
n
a
i
)
−
(
x
−
1
)
×
∑
i
=
1
n
⌊
a
i
x
⌋
\begin{aligned} &\sum_{i=1}^n a_i-x\times \lfloor \frac {a_i} x \rfloor+\lfloor \frac {a_i} x \rfloor\\ &=(\sum_{i=1}^n a_i)-(x-1)\times\sum_{i=1}^n \lfloor \frac {a_i} x \rfloor \end{aligned}
i=1∑nai−x×⌊xai⌋+⌊xai⌋=(i=1∑nai)−(x−1)×i=1∑n⌊xai⌋
枚举 x x x,后面那一串可以 O ( n x ) O(\dfrac n x) O(xn) 求,总时间复杂度就是 O ( n ln n ) O(n\ln n) O(nlnn)。
代码如下:
#include <cstdio>
#define maxn 1000010
#define ll long long
int n,a[maxn],b[maxn];
ll sum=0,ans=999999999999999,tot;
int min(int x,int y){return x<y?x:y;}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),sum+=a[i],b[a[i]]++;
for(int i=1;i<=maxn-10;i++)b[i]+=b[i-1];
for(int i=1;i<=maxn-10;i++)
{
tot=0;
for(int j=i;j<=maxn-10;j+=i)
tot+=(ll)(b[min(j+i-1,maxn-10)]-b[j-1])*(j/i);
if(tot*(1-i)<ans)ans=tot*(1-i);
}
printf("%lld",ans+sum);
}
B
不难发现一个性质,如果 a i < a j a_i<a_j ai<aj 且 a i a_i ai 在 a j a_j aj 前面,那么模了 a i a_i ai 后无论 a j a_j aj 在哪里都无所谓,因为 x x x 已经小于 a j a_j aj 了。
所以可以将所有数从大到小排个序后依次考虑,设 f i , j f_{i,j} fi,j 表示模了前 i i i 个数后值为 j j j 的方案数,考虑第 i i i 个数是否有贡献,有就直接模,令 f i , j m o d a i + = f i − 1 , j f_{i,j\bmod a_i}+=f_{i-1,j} fi,jmodai+=fi−1,j,没有就将它放到比他小的一个 a j a_j aj 后面,方案数为 n − i n-i n−i,使 f i , j + = f i − 1 , j × ( n − i ) f_{i,j}+=f_{i-1,j}\times (n-i) fi,j+=fi−1,j×(n−i) 即可。
代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long
#define mod 998244353
int n,x,a[1010];
ll f[1010][5010];
bool cmp(int x,int y){return x>y;}
int main()
{
scanf("%d %d",&n,&x);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
sort(a+1,a+n+1,cmp);
f[0][x]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=x;j++)
f[i][j%a[i]]=(f[i][j%a[i]]+f[i-1][j])%mod,
f[i][j]=(f[i][j]+f[i-1][j]*(n-i)%mod)%mod;
for(int i=x;i>=0;i--)
if(f[n][i]>0){printf("%d\n%lld",i,f[n][i]);return 0;}
}
C
千辛万苦看懂了题解,然后标程就几乎把我劝退了……
硬看了一两个小时,最后还是决定跑路,以后可能会来补吧。