[UOJ摸鱼]UOJ Round #1解题报告(未完)
[UOJ摸鱼]UOJ Round #1解题报告
前言
还有好多题没补,先摸会儿鱼~
缩进优化
链接
题解
先推柿子!
要算的是找一个\(K\)使得\(\sum_{i=1}^{n}{\lfloor \frac{a_n}{K} \rfloor + a_n mod K}\)最小
化简一下就是\(\sum_{i-1}^{n}{a_i - (K-1)\times \lfloor \frac{a_n}{K} \rfloor}\)
显然\(K\)应该满足\(1\leq K \leq max(a_i)\) 。
当我们确定一个\(K\)的时候,\(1\)到\(max(a_i)\)内最多有\(\lfloor \frac{max(a_i)}{K} \rfloor\)个不同的\(\lfloor \frac{x}{K} \rfloor\)的值。
于是我们直接枚举\(K\),算出所有可能答案取个min就行。效率是\(O(max(a_i)log(max(a_i)))\)。
\(Code\)
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const LL P=998244353;
const int N=1e6+10;
int n;
LL a[N],c[N];
LL ans,sum,mx,res,now;
int main(){
scanf("%d",&n);sum=0;mx=0;
for(int i=1;i<=n;++i) {
scanf("%lld",&a[i]);
sum+=a[i];mx=max(mx,a[i]);++c[a[i]];
}
for(int i=1;i<=mx;++i) c[i]+=c[i-1];
ans=sum;
for(LL k=2;k<=mx;++k){
now=0;
for(LL l=k,r=k+k-1;l<=mx;l+=k,r+=k){
r=min(r,mx);
now+=(c[r]-c[l-1])*(l/k);
}
now=now*(k-1);
res=sum-now;
ans=min(ans,res);
}
printf("%lld\n",ans);
return 0;
}
外星人
链接
题解
首先可以发现我们对一个数取模多次相当于一次。。一个数对比它大的数取模不变。
第一步一定是排序emm
然后考虑第一问怎么做,由于\(n\)和\(a_i\)都不大,考虑\(O(n^2)\)的递推。
如果\(i\)可以被模出来,那么再用它来对所有\(a_i\)取次一模,找到新的能被模出来的数。
最后找到最大的比a_1小的能模出来的数,就是第一问的答案了。记为ans1。
然后是第二问,和第一问思路类似。
用第一问答案反推。令\(f_i\)表示在\(i\)的时候,只考虑比\(i\)小的模数,最后得到ans1的方案数。
那么我们在算\(f_i\)的时候,只需要枚举第一个用来模的数,然后乘个组合数什么的就行了。
总效率是\(O(n^2)\)的。
\(Code\)
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const LL P=998244353;
const int N=1e6+10;
int n,X;
int a[5005];
bool vis[5005];
LL f[5005];
int cnt[5005];
LL jc[5005];
LL ny[5005];
LL C(int x,int y){
if(y>x) return 0;
return jc[x]*ny[y]%P*ny[x-y]%P;
}
LL add(LL &x,LL y){
x+=y;if(x>=P)x-=P;
}
int main(){
jc[0]=jc[1]=ny[0]=ny[1]=1;
for(LL i=2;i<=5000;++i){
jc[i]=jc[i-1]*i%P;
ny[i]=(P-P/i)*ny[P%i]%P;
}
for(LL i=2;i<=5000;++i){
ny[i]=ny[i-1]*ny[i]%P;
}
scanf("%d%d",&n,&X);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
}
sort(a+1,a+1+n);
vis[X]=1;
for(int i=X;i>=1;--i){
if(vis[i])
for(int j=1;j<=n;++j){
if(i>=a[j]) vis[i%a[j]]=1;
}
}
int ans1=0;
for(int i=1;i<a[1];++i) if(vis[i]) ans1=i;
printf("%d\n",ans1);
for(int i=1,j=0;i<=X;++i){
while(j<n&&a[j+1]<=i) ++j;
cnt[i]=j;
}
f[ans1]=1;
int Y;
for(int i=ans1+1;i<=X;++i){
for(int j=n;j>=1;--j){
if(a[j]>i) continue;
Y=i%a[j];
add(f[i],f[Y]*C(cnt[i]-1,cnt[Y])%P*jc[cnt[i]-1-cnt[Y]]%P);
}
}
LL ans=f[X];
ans=ans*C(n,cnt[X])%P*jc[n-cnt[X]]%P;
cout<<ans<<endl;
return 0;
}
这个仙人掌太难了,还没写QAQ