题目链接
题目大意:给出n个巧克力的单价分别是多少,给出买巧克力的最低价l, 最高价r, 以及总钱数,求最多能买多少。
贪心
排序,从符合范围的最低价开始买即可
#include
using namespace std;
typedef long long LL;
int main(void)
{
int T;
scanf("%d", &T);
while(T --)
{
int n;
scanf("%d", &n);
vector<int> vec(n);
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
for(int i = 0; i < n; i ++)
{
scanf("%d", &vec[i]);
}
sort(vec.begin(), vec.end());
int sum = 0;
int ans = 0;
for(int i = 0; i < n; i ++)
{
if(vec[i] >= l && vec[i] <= r && sum + vec[i] <= k)
{
sum += vec[i];
ans ++;
}
}
printf("%d\n", ans);
}
return 0;
}
题目大意:给出n个点,给出去每个点的次数,现在要求重新排坐标,使得去各点距离 * 次数的和最小
思维、贪心
假设原点就是起点,按照次数排序,在原点两端分配,次数最高的排在距离原点最近即可。
#include
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef pair<LL, int> PII;
int main(void)
{
int T;
scanf("%d", &T);
while(T --)
{
int n;
scanf("%d", &n);
vector<PII> vec(n);
for(int i = 0; i < n; i ++)
{
scanf("%lld", &vec[i].x);
vec[i].y = i;
}
sort(vec.begin(), vec.end());
int left = 1, right = 1;
LL ans = 0;
vector<int> res(n);
for(int i = n - 1; i >= 0; i --)
{
if(i % 2)
{
ans += 2 * left * vec[i].x;
res[vec[i].y] = -left;
left ++;
}
else
{
ans += 2 * right * vec[i].x;
res[vec[i].y] = right ++;
}
}
printf("%lld\n", ans);
printf("%d", 0);
for(int i = 0; i < n; i ++)
{
printf(" %d", res[i]);
}
puts("");
}
return 0;
}
题目大意:对于一个序列a, 给出m段下标从l 到r的异或值。求出整个a序列的异或值
位运算、组合数学
感悟
只要有大于等于1个数第i位为1, 对答案的贡献是相同的
#include
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef pair<LL, int> PII;
const int N = 2e5 + 10, mod = 1e9+7;
int main(void)
{
int T;
scanf("%d", &T);
vector<LL> vec(N);
vec[0] = 1;
for(int i = 1; i < N;i ++)
{
vec[i] = (vec[i - 1] * 2ll) % mod;
}
while(T --)
{
int n, m;
scanf("%d%d", &n, &m);
LL ans = 0;
while(m --)
{
int l, r;
LL x;
scanf("%d%d%lld", &l, &r, &x);
ans |= x;
}
cout << (ans * vec[n - 1]) % mod << endl;
}
return 0;
}
题目描述:给出一个数组,可以重新排列数组中的数,使得gcd(a[1], a[2]) + gcd(a[1], a[2], a[3]) + gcd(a[1], a[2], a[3]…a[n]) 最大,求次最大值
贪心、dp、gcd的性质
分析:
dp
, (我也没想到。。。)dp[1] = cnt[1] = 4, dp[2] = 2 * 3 + 1 = 7, dp[6] = dp[2] + (6 - 2) * 1 = 11
处理完后,我们求一遍dp[i]的最大值即可。
#include
#define x first
#define y second
using namespace std;
typedef long long LL;
const int N = 1e5 + 10, M = 5e6 + 10;
int a[N];
LL cnt[M], dp[M];
int main(void)
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]), cnt[a[i]] ++;
for(int i = 1; i < M; i ++)
{
for(int j = i + i; j < M; j += i)
{
cnt[i] += cnt[j];
}
}
LL ans = 0;
dp[1] = cnt[1];
for(int i = 1; i < M; i ++)
{
for(int j = i + i; j < M; j += i)
{
dp[j] = max(dp[j], dp[i] + cnt[j] * (j - i));
}
}
// cout << dp[1] <<" " <
for(int i = 1; i < M; i ++)
{
ans = max(ans, dp[i]);
}
cout << ans <<endl;
return 0;
}
题目大意与D1相同,但数据范围扩大到1e7。
一个优化:我们不全部枚举x的整数倍,而是x的素数倍数
。因为若tx不是x的素数倍数,即t不是素数,tx和x之间还可以加别的数。
代码优化
cnt数组的处理:输入时对每个数计数。
cnt数组整体与上题意义一样,但不同点:此题tx为素数倍数,因此枚举素数t, 更新cnt数组。
dp数组的含义与上题相同,但因为枚举的素数倍数tx,因此x前必须确定紧跟着哪个素数倍数最优,这便是第二重循环的含义。
i从大到小枚举,这样最后确定的dp[1],确定了1前跟的最优数, dp[1]就是答案。
解释temp:算出加入这个素数倍数在x前面,会贡献多少
#include
#define x first
#define y second
using namespace std;
const int N = 2e7 + 10;
long long cnt[N], dp[N];
long long primes[N];
bool book[N];
int t, n;
int main()
{
//预处理质数数组
for(int i = 2; i <= N - 1;i ++)
{
if(!book[i])
{
primes[t ++] = i;
for(int j = i * 2; j < N; j += i)
{
book[j] = 1;
}
}
}
scanf("%d", &n);
int num;
for(int i = 1; i <= n; i ++) scanf("%d", &num), cnt[num] ++;
for(int i = 0; i < t; i ++)
{
int p = primes[i]; //当前素数,
for(int j = N / p; j; j --) //枚举每一个数,最大是 N / p
{
cnt[j] += cnt[j * p];
}
}
for(int i = N - 1; i; i --){
long long temp = 0;
for(int j = 0; j < t && i * primes[j] < N; j ++)
{
temp = max(temp, dp[i * primes[j]] - cnt[primes[j] * i] * i);
}
dp[i] = temp + cnt[i] * i;
}
cout << dp[1] <<endl;
return 0;
}
昨天是1.12日,晚上累了没写完这篇博客,今天早上重新思考的D2,发现思路仿佛比昨天清晰了。。。坚持下去,cf想上大大大大分,,嘤嘤嘤