2024-09-02-ABC369
A - 369
给定两个数字,问有多少个不同的数字x,使这三个数字排一下序是等差数列。
按差分类讨论一下即可。
#include<bits/stdc++.h>
using namespace std;
int x,y;
int main()
{
cin>>x>>y;
if(x>y) swap(x,y);
if(x==y) puts("1");
else if((y-x)%2) puts("2");
else puts("3");
return 0;
}
B - Piano 3
一个人两只手弹钢琴,他有一个疲劳值,每当一只手从x键移动到y键,疲劳值就会增加|x-y|,求最后疲劳值的总和。
4
3 L
6 R
9 L
1 R
11
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
vector<int> l,r;
int main()
{
int n;
cin>>n;
while(n--)
{
int x;
char c;
cin>>x>>c;
if(c=='L') l.pb(x);
else r.pb(x);
}
int ans=0;
for(int i=1;i<l.size();i++)
ans+=abs(l[i]-l[i-1]);
for(int i=1;i<r.size();i++)
ans+=abs(r[i]-r[i-1]);
cout<<ans;
return 0;
}
C - Count Arithmetic Subarrays
给定一个数组,问里面有多少个LR,满足下标从L到R的数列是等差数列。
对于每一个数记录一个dp[i],表示第i个数前最长的等差数列是多长,维护这个数组:每次只要判断新的数能否接在前面的等差数列后面,如果能就是dp[i]=dp[i-1]+1,否则就直接等于2。
那么对于这一个数,以他为右端点的等差数列数量就是dp[i]个,所以答案就是dp数组的总和。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,a[200005];
ll dp[200005];
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
if(n==1)
{
puts("1");
return 0;
}
dp[0]=1;
dp[1]=2;
ll d=a[1]-a[0],ans=3;
for(int i=2;i<n;i++)
{
if(a[i]-a[i-1]==d) dp[i]=dp[i-1]+1;
else {
dp[i]=2;
d=a[i]-a[i-1];
}
ans+=dp[i];
}
cout<<ans;
return 0;
}
D - Bonus EXP
有一排怪,每个怪打掉有个经验值x,高桥按顺序对于每一个怪可以考虑打或者不打,跳过当前怪就没有经验值,不跳过就会获得x的经验值,并且如果这是他打掉的第偶数个怪物,他获得的经验值会变成双倍,问他最多能获得多少经验值。
考虑dp,对于每一个怪我们肯定都是考虑当前怪是把他放在奇数打还是偶数打,所以我们维护一个dp1表示到目前为止打了奇数个怪物能获得的最大经验值,dp2表示到目前为止打了偶数个怪物能获得的最大经验值,那么对于一个新的怪物,考虑怎么更新dp1和dp2,如果把当前怪当偶数打,那么就是dp2=max(dp2,dp1+x*2),当奇数打就是dp1=max(dp1,dp2+x),还是很直观的。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=200005;
ll a[maxn],dp1,dp2;
int n;
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
ll t=dp1;
dp1=max(dp1,dp2+a[i]);
if(i) dp2=max(dp2,t+2*a[i]);
}
cout<<max(dp1,dp2)<<endl;
return 0;
}
E - Sightseeing Tour
有一张n个节点m条边的双向图,每一条边上有通过所需要的时间,有q次询问,每次询问给不超过五条边,求这五条边至少每个都走了一次的情况下,从1到n的最短路径长度。
首先用floyd求出全图最短路,然后全排列五条边走的顺序,对于每一条边的两个端点ab,我们有可能是从a走到b也有可能是从b走到a,所以每一条边正反都要计算一下,然后记得算头的时候要从1出发,最后要加上最后一个点到n的距离。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for(int i = 0; i < n; ++i)
#define ll long long
#define N 400
#define M (int)2e+5
#define INF (ll)1e+15
int n;
int u[M],v[M];
ll t[M];
ll d[N][N];
int k;
vector<int>a;
ll solve(void){
ll ans=INF;
ll dp[5][2];
vector<int>b;
rep(i,k)b.push_back(i);
while(true){
dp[0][0]=d[0][v[a[b[0]]]]+t[a[b[0]]];
dp[0][1]=d[0][u[a[b[0]]]]+t[a[b[0]]];
rep(i,k-1){
dp[i+1][0]=min(dp[i][0]+d[u[a[b[i]]]][v[a[b[i+1]]]],dp[i][1]+d[v[a[b[i]]]][v[a[b[i+1]]]])+t[a[b[i+1]]];
dp[i+1][1]=min(dp[i][0]+d[u[a[b[i]]]][u[a[b[i+1]]]],dp[i][1]+d[v[a[b[i]]]][u[a[b[i+1]]]])+t[a[b[i+1]]];
}
ans=min(ans,dp[k-1][0]+d[u[a[b[k-1]]]][n-1]);
ans=min(ans,dp[k-1][1]+d[v[a[b[k-1]]]][n-1]);
if(!(next_permutation(b.begin(),b.end())))break;
}
return ans;
}
int main() {
int m,q,x;
cin>>n>>m;
rep(i,n)rep(j,n){
if(i==j)d[i][j]=0;
else d[i][j]=INF;
}
rep(i,m){
cin>>u[i]>>v[i]>>t[i];
u[i]--,v[i]--;
d[u[i]][v[i]]=min(d[u[i]][v[i]],t[i]);
d[v[i]][u[i]]=min(d[v[i]][u[i]],t[i]);
}
rep(i1,n)rep(i0,n)rep(i2,n)d[i0][i2]=min(d[i0][i2],d[i0][i1]+d[i1][i2]);
cin>>q;
rep(qq,q){
cin>>k;
a.clear();
rep(kk,k){
cin>>x;
a.push_back(x-1);
}
cout<<(solve())<<endl;
}
return 0;
}
F - Gather Coins
一张H*W的网格上,有N个点上有金币,你一开始在左上角,左上角的坐标是0,0,每次可以向下走或者向右走,问到达H,W时,你最多能捡到多少金币,并且输出操作序列。
不难发现要从一个金币点到另一个金币点的条件是后一个点的坐标x,y均大于等于前一个点的坐标,那么就是一个很裸的最长不下降子序列的dp了,把所有的金币点按照横坐标排序,那么最多的金币数就是求纵坐标的最长不下降子序列,然后拿出这些坐标,坐标之间模拟一下R和D是怎么走的就可以了。
#include <bits/stdc++.h>
using namespace std;
int main() {
int h, w, n;
cin >> h >> w >> n;
vector<pair<int, int>> coins;
for (int i = 0; i < n; i++) {
int r, c;
cin >> r >> c;
coins.emplace_back(r, c);
}
sort(coins.begin(), coins.end());
vector<int> dp(n, 1e9), id(n, -1), pre(n);
for (int i = 0; i < n; i++) {
int it = upper_bound(dp.begin(), dp.end(), coins[i].second) - dp.begin();
dp[it] = coins[i].second;
id[it] = i;
pre[i] = (it ? id[it - 1] : -1);
}
int m = n - 1;
while (id[m] == -1) --m;
vector<pair<int, int>> path = {{h, w}};
int now = id[m];
while (now != -1) {
path.push_back(coins[now]);
now = pre[now];
}
path.emplace_back(1, 1);
reverse(path.begin(), path.end());
string s;
for (int i = 0; i < (int) path.size() - 1; i++) {
int d = path[i + 1].first - path[i].first;
int r = path[i + 1].second - path[i].second;
while (d--) s.push_back('D');
while (r--) s.push_back('R');
}
cout << m + 1 << '\n' << s << '\n';
}