今天挂得还是比较惨的。
先讲一下题目大意吧。
第一题:给N(N <= 100)种不同面值(<=1000)的硬币(你可以假设每种都有无数多个),有T(<=100000)个询问,问你要组成面值为M(<=10^16),最小要多少个硬币。
第二题:给出x(x<=10^12),问你最小的y,使得φ(y) = x.
第三题,给你一个n(n<=5000)个元素的数列A和一个常数D,求对于每个位置i,有多少种合法的三元组(k,j,l)
一个三元组被称为合法的。当且仅当max(0,i-D) < k <= j <= l < i,且a[k]+a[j]+a[l] = a[i]
我们按题目难度排序。很显然难度是倒序的。
先讲一下我考场时怎么想的吧。
第一题,很容易得到DP式f[i] = min{f[i - A[j]] + 1},然后,我是这么想的。
既然M这么大,很直接的思路就是用矩阵。结果,面值太大了(<=1000)。。一个用到N的矩阵搞不出来。。。想了一会儿后果断放弃,捡了暴力的30分。
第二题,看起来就是数学题。
我们知道的是,φ(n) = π(pi - 1)(pi^(ci-1)).
很直接的想法就是把x给分解质因数。要使得y最小,那么ci肯定尽量为1啦(注意这里。。。)
那么我们对于x的约数i,若(i+1)为质数,则称i为x的伪质因数。
(我漏掉的地方。。)若x mod (2^k) = 0,那么我们此时也要把2^k作为x的伪质因数。。。
接着我们直接爆搜就好了。(剪枝就不用讲了吧)
第三题,看起来像是要用数据结构。于是我光荣跳坑。想了很久的可持久化。空间始终不够。
最后只剩一个多钟去搞60分。。。结果。。。。没有拍。。。你懂的。
题解(我就简洁点了)
第一题,注意到面值<=1000,对于Dp,我们Dp的范围越大,F数组越趋于稳定。事实上我们只需要DP到
max^2就好了。对于询问M,我们选择一定量的max,使得M降到max^2以内,直接+起来就好了。
第二题,上面讲了。
第三题,设g[i] 表示当前合法区间内两个数和为i的方案,f[i]表示区间内单个数为i的个数。
那么我们枚举l,Ans+=g[A[i] - A[l]] + f[A[i] - A[l] - A[l]] + (3 * A[l] = A[i])
你或许会问我最后那个有什么用。我会告诉你这是为了让每一种方案*3倍。
最后我们Ans/3就去重了。
今天暴露的问题:
1:数组空间开小,分析问题不全面。
2:做题时有点心急,敲代码没有注意具体的时间复杂度与空间复杂度。
3:容易陷入死坑。想问题复杂。
努力方向:
1:注意思维的多样化,不要因为做专题做多了人傻了。
2:敲代码时不要急,思考问题要缜密
3:修改一段代码时注意引起的空间与时间问题。
贴代码
T1
#include
#include
#include
using namespace std;
const int MAXN = 100005;
int F[2000005];
int A[105],N;
int main()
{
scanf("%d", &N);
memset(F,255,sizeof F);
F[0] = 0;
for(int i = 1;i <= N;i ++) scanf("%d", &A[i]);
sort(A + 1,A + N + 1);
for(int i = 1;i <= 1000000;i ++)
for(int j = 1;j <= N;j ++) if (i >= A[j] && F[i - A[j]] != -1)
{
if (F[i] == -1) F[i] = F[i - A[j]] + 1; else F[i] = min(F[i],F[i - A[j]] + 1);
}
int T;scanf("%d", &T);
for(int i = 1;i <= T;i ++)
{
long long x,ans;scanf("%lld", &x);
if (x <= 1000000) {printf("%d\n", F[x]);continue;}
ans = (x - 1000000) / A[N];x -= ans * A[N];
while (F[x] == -1 && ans) ans ++,x -= A[N];
if (!ans && F[x] == -1) printf("-1\n"); else
printf("%lld\n",F[x] + ans);
}
}
T2
#include
#include
#include
#include
T3
#include
#include
#include
#include