本次开出了3题 第4题有思路开始写不出来 具体原由可能是因为我的码风比较差 写到后面我都不知道这么写了
对于这道题 关键的点在于l≤LCM(x,y)≤r
为了尽可能满足这个式子我们可以发现一点:我们要让y尽可能的小
我们可以知道的一点是 lcm=x*y/gcd(x,y),假如此时lcm<=y的话 我们要求x==gcd(x,y) 即y是x的倍数时 我们可以将此式子维护位 x<=lcm<=y (设x<y)所以此时最小的y是由最小的x决定的:
ymin=2*xmin xmin=l
所以当r>2*l时无解 其他情况输出 l,2*l就好
这一题的话 我写了20多分钟 本来是想写dp的 结果写不出来 我就直接暴力了
这道题的本质在于这个z仅有5 我们可以枚举每个点回退的次数(可以发现他最多在一个点有回退)
此时我们考虑为什么在一个点回退最好:
假设我们可以在2不同的点开始回退 那么此时我们可以发现一个 每次回退的话回产生一个值a[i]+a[i-1]对于任意两个点回退的话 起码有一个大一个小或相同 那么我们就可以得知 我们将一个点回退此时全部给另一个(价值大的)就好了 这样保证不会差
但是要注意的是我们不一定要将所有回退此时都用完 所以此时我们就要枚举在一个点的回退次数去最大就好了
#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
const int maxn = 1e5 + 10;
int a[maxn], n, m,k,z;
int sum[maxn];
int dp[maxn][6];//dp[i][j]前i次用到j次回退可以获得最大值
void solve()
{
cin >> n >> k >> z;
for (int i = 1; i <= n; i++) {
cin >> a[i];//对于每一位我们都考虑将z全部用来回去
sum[i] = sum[i - 1] + a[i];
}
int ans = 0;
for (int i = 2; i <=k+1; i++)
{
int now = a[1]+sum[i]-sum[1];
for (int j = 0; j <= z; j++)
{
if (2 * j + i - 1 <= k)//代表回退后可以继续走
{
int now1 = now + j * (a[i] + a[i - 1]);
int left = k - 2 * j - i + 1;
now1 += sum[i + left] - sum[i];
ans = max(ans, now1);
}
else//代表不能走回去
{
int left = k - i + 1;
int now1 = now;
for (int j1 = 1; j1 <= left; j1++)
{
if (j1 % 2)
now1 += a[i - 1];
else
now1 += a[i];
}
ans = max(ans, now1);
}
}
}
cout << ans<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--)
solve();
}
C:这道题我写了大概20分钟 主要是想歪了一点 其实这里的思路比较明显 我们考虑要维护两个数字 这两个数字循环出现 就是类似:2525252525这类数字 当我先的是维护nex[i][j]代表点i后面最近的j的位置在哪里 本来也没事大问题就是写麻烦一点 当我写错了
然后我就发现其实暴力就好根本不用维护这个东西 就是我们跳出s中我们指定的两个数的序列
代码如下:
#include<iostream>
#include<vector>
#include<string>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
const int maxn = 1e5 + 10;
int a[maxn], n, m,k,z;
//维护一个nex[i][j] 代表i位置下一个j(数目)在哪里
pair<int, int>pii[110];
int cnt = 0;
void solve()
{
string s;
cin >> s;
int len = s.length();
int ans =0;
for (int i = 1; i <=cnt; i++){
int len1 = 0;
int now =1;
for (int j = 0; j < len; j++)
{
if (now == 1 && s[j] == pii[i].first+'0')
{
len1++;
now = 2;
}
else if (now == 2 && s[j] == pii[i].second+'0')
{
len1++;
now = 1;
}
}
if (pii[i].first == pii[i].second)
{
ans = max(ans, len1);
}
else
{
if (len1 % 2)
{
ans = max(ans, len1 - 1);
}
else
{
ans = max(ans, len1);
}
}
}
cout << len - ans <<'\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
for (int i = 0; i <= 9; i++)
{
for (int j = 0; j <= 9; j++)
pii[++cnt].first = i, pii[cnt].second = j;
}
cin >> t;
while (t--)
solve();
}
D:D题的话 其实贪心比较好想 就是我们假如感刚开始的区间交集不足k的话 我们就贪心的去扩大交集 因为我们此时每次扩大1个交集的代价要么是1 要么是2 所以我们先1 后2 就好了
但我写不出来(写假了 连案例都过不去)
这里参照其他大佬的代码写一份代码 并做出调整 且学习差别
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int maxn = 2e5 + 10;
int n, k, l1, r1, l2, r2;
void solve()
{
cin >> n >> k;
cin >> l1 >> r1;
cin >> l2 >> r2;
if (l1 > l2)
{
swap(l1, l2);
swap(r1, r2);
}//将小的l设为l1
int cross = 0;
if (r1 > l2&&l1 < r2)//有交集
{
cross = min(r1, r2) - max(l1, l2);
}
int now = n * cross;
k -= now;//代表剩下要维护的交集
if (k <= 0)
{
cout << 0<<endl;
return;
}
//此时还没有成功,我们考虑一下一个一个维护 先处理我们要让其到达相交临界的步数
int need = l2-r1;
if (need < 0)
need = 0;
//开始维护
int maxx =max(r1,r2)-min(l1,l2)-cross;//每个区间最多可以维护多少个1步区域
//cout << need << ' ' << maxx << ' ' << cross << endl;
int tmp = 1e18;
now = 0;//此时的步数
for (int i = 1; i <= n; i++)
{
now += need;
if (maxx >= k)//代表此时我们可以直接维护成功
{
now += k;
k = 0;
break;
}
else
{
now += maxx;
k -=maxx;
tmp = min(tmp, now + 2 * k);//假设剩下的都利用2步来维护
}
}
if (k > 0)//即我们所有都利用1步是不可以的
{
cout << tmp << endl;
}
else
{
cout << min(now, tmp) << endl;
}
}
signed main()
{
int t;
cin >>t;
while (t--)
solve();
}
E:
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int maxn = 1e5 + 10;
int gcd(int a, int b)
{
if (b == 0)
return a;
else
return gcd(b, a%b);
}
void solve()
{
int m, d, w;
cin >> m >> d >> w;
int p = min(m, d);
d--;
int k = gcd(w, d);
w /= k;
//cout << k << endl;
int imax = (p - 1) / w;
int ans = (imax)*p - w * (imax + 1)*(imax) / 2;
cout << ans << endl;
}
signed main()
{
int t;
cin >>t;
while (t--)
solve();
}
总结:
1.我写的代码是基于全部一起变 这样的话不太好维护 像大佬写的一步一步改变的话任意查找错误
还有就是我没有维护将其变为交叉时的代价 这点非常重要 其次就是我考虑最后两步的次数时是瞎搞的(我也记不得我是咋写的)像大佬写的那样是非常好的(就是维护一个后缀 假如将剩下的所有都变为2步时)这样有效的减小了代码量还有思考量 是一个非常值得学习的写法