A - Glutton Takahashi
题意:
给定
n
n
n个字符串,问是否有两个相邻的 sweet
解题思路:
遍历判断当前字符串与上一个字符串是否都为sweet
即可。
代码:
B - Grid Walk
题意:
给定一个二维网格,有障碍物,有空地,给定初始位置。
给定移动操作,若移动目标位置为空地则移动,否则留在原地。
问最终位置。
解题思路:
模拟即可
代码:
C - Minimum Glutton
题意:
n n n个食物,有咸度和甜度。安排一个吃的顺序,使得吃的食物尽可能少,且一旦咸度 > x >x >x或甜度 > y >y >y就停下来不吃。
解题思路:
代码:
在这里插入代码片
D - K-th Nearest
题意:
一维坐标,给定$n个点,依次回答 q q q个询问。每个询问给定一个位置,问距离该位置第 k k k近的点的距离是多少。
解题思路:
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
void solve()
{
int n, q;
cin >> n >> q;
vector<int> a(n + 1);
for (int i = 1; i <= n; ++i)
cin >> a[i];
sort(a.begin() + 1, a.end());
int b, k;
while (q--)
{
// 二分枚举距离
cin >> b >> k;
int l = 0, r = 2e8, mid;
while (l < r)
{
mid = l + r >> 1;
// 如果不用得到具体下标,则可以用 auto,这样就可以免得减去起始位置了
auto lb = lower_bound(a.begin() + 1, a.end(), b - mid);
auto rb = upper_bound(a.begin() + 1, a.end(), b + mid);
int all = rb - lb;
if (all >= k)
r = mid;
else
l = mid + 1;
}
cout << l << '\n';
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
// cin >> t;
while (t--)
solve();
return 0;
}
E - Maximum Glutton
题意:
解题思路:
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
void solve()
{
int n, x, y;
cin >> n >> x >> y;
int a, b;
vector<vector<int>> dp(n + 1, vector<int>(x + 1, 0x3f3f3f3f));
for (int i = 0; i <= x; ++i)
dp[0][i] = 0;
for (int i = 1; i <= n; ++i)
{
cin >> a >> b;
for (int j = i; j >= 1; --j)
for (int k = x; k >= a; --k)
dp[j][k] = min(dp[j][k], dp[j - 1][k - a] + b);
}
for (int j = n - 1; j >= 0; --j)
if (dp[j][x] <= y)
{
cout << j + 1;
return;
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
// cin >> t;
while (t--)
solve();
return 0;
}
在这里插入代码片
F - Range Connect MST
题意:
解题思路:
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
bool cmp(const vector<int> &x, const vector<int> &y)
{
return x[2] < y[2];
}
int fa[200010];
int find(int x)
{
if (x == fa[x])
return x;
return fa[x] = find(fa[x]);
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, q;
cin >> n >> q;
for (int i = 1; i <= n; ++i)
fa[i] = i;
vector<vector<int>> a(q, vector<int>(3));
for (int i = 0; i < q; ++i)
cin >> a[i][0] >> a[i][1] >> a[i][2];
sort(a.begin(), a.end(), cmp);
int ans = 0;
int cnt = 0;
for (int i = 0; i < q; ++i)
{
int l = a[i][0];
int r = a[i][1];
int c = a[i][2];
ans += c;
for (int j = find(l) + 1; j <= r; j = find(j) + 1)
{
cnt++;
ans += c;
fa[j - 1] = j;
}
}
if (cnt != n - 1)
cout << -1;
else
cout << ans;
return 0;
}
在这里插入代码片
G - Last Major City
题意:
解题思路:
代码:
H - Spoiler
题意:
给定一个字符串,包含两个|
,将|
和两个|
之间的字符消去。
解题思路:
模拟即可
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+100,mod=998244353;
typedef long long ll;
typedef pair<pair<int,int>,int> PII;
int T;
void solve()
{
string op;
cin>>op;
int l=-1,r=-1;
for(int i=0;i<op.size();i++)
{
if(l==-1&&op[i]=='|') l=i;
else if(op[i]=='|'&&l!=-1) r=i;
}
for(int i=0;i<op.size();i++)
{
if(i>=l&&i<=r) continue;
else cout<<op[i];
}
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
I - Delimiter
题意:
给定 n n n个数,倒序输出。
解题思路:
用数组储存,之后直接倒着输出
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+100,mod=998244353;
typedef long long ll;
typedef pair<pair<int,int>,int> PII;
int T;
void solve()
{
vector<int> res;
int x;
while(1)
{
cin>>x;
res.push_back(x);
if(x==0) break;
}
reverse(res.begin(),res.end());
for(auto i : res) cout<<i<<endl;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
J - A+B+C
题意:
给定三个数组
a
,
b
,
c
a,b,c
a,b,c,回答
q
q
q次询问。
每次询问,给定
x
x
x,问能否从三个数组各选一个数,其和为
x
x
x 。
解题思路:
由于数据范围很小,可以直接预处理出能组合出来的数,最后判断即可
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+100,mod=998244353;
typedef long long ll;
typedef pair<pair<int,int>,int> PII;
int T;
int a[N];
int b[N],c[N];
int x[N];
map<int,bool> mp;
void solve()
{
int n,m,l,q;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
cin>>m;
for(int i=1;i<=m;i++) cin>>b[i];
cin>>l;
for(int i=1;i<=l;i++) cin>>c[i];
cin>>q;
for(int i=1;i<=q;i++) cin>>x[i];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
for(int k=1;k<=l;k++)
{
int s=a[i]+b[j]+c[k];
mp[s]=1;
}
}
}
int ans=0;
for(int i=1;i<=q;i++)
{
if(mp[x[i]]) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
K - String Bags
题意:
有n个袋子,每个袋子里有若干个字符串。
给定一个目标串 t,要求从每个袋子选出最多 1个字符串,按顺序拼接成 t。
问取出来的字符串个数的最小值。
解题思路:
类似于01背包,每一个袋子可以取也可以不取。
令
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示从前
i
i
i个袋子里取出能拼成前
j
j
j个字符的最小次数
状态转移方程为:
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
]
,
d
p
[
i
−
1
]
[
j
−
l
e
n
]
+
1
)
dp[i][j]=min(dp[i][j],dp[i-1][j-len]+1)
dp[i][j]=min(dp[i][j],dp[i−1][j−len]+1)
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+100,mod=998244353;
typedef long long ll;
typedef pair<pair<int,int>,int> PII;
int T;
string t;
int n;
string op[110][11];
int dp[110][110];
int c[N];
void solve()
{
cin>>t;
int ls=t.size();
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>c[i];
for(int j=1;j<=c[i];j++) cin>>op[i][j];
}
for(int i=0;i<=109;i++)
for(int j=0;j<=109;j++)
dp[i][j]=1e3;
dp[0][0]=0;
for(int i=1;i<=n;i++)
{
dp[i][0]=0;
for(int len=0;len<=ls;len++)
{
dp[i][len]=dp[i-1][len];
for(int j=1;j<=c[i];j++)
{
int l=op[i][j].size();
if(len<l) continue;
string kk=t.substr(len-l,l);
if(op[i][j]==kk)
{
dp[i][len]=min(dp[i][len],dp[i-1][len-l]+1);
}
}
}
}
if(dp[n][ls]>=1e3) cout<<"-1"<<endl;
else cout<<dp[n][ls]<<endl;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
发现 d p dp dp数组第一维只由前一个状态更新过来的,所以可以用滚动数组优化:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+100,mod=998244353;
typedef long long ll;
typedef pair<pair<int,int>,int> PII;
int T;
string t;
int n;
string op[110][110];
int dp[1010];
int c[N];
void solve()
{
cin>>t;
int ls=t.size();
cin>>n;
for(int i=1;i<=n;i++)
{
int s;
cin>>s;
c[i]=s;
for(int j=1;j<=s;j++) cin>>op[i][j];
}
for(int j=0;j<=110;j++)
dp[j]=1e9;
dp[0]=0;
for(int i=1;i<=n;i++)
{
for(int len=ls;len>=0;len--)
{
for(int j=1;j<=c[i];j++)
{
int l=op[i][j].size();
if(len<l) continue;
string kk=t.substr(len-l,l);
if(op[i][j]==kk)
{
dp[len]=min(dp[len],dp[len-l]+1);
}
}
}
}
if(dp[ls]==1e9) cout<<"-1"<<endl;
else cout<<dp[ls]<<endl;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
L - Insert or Erase
题意:
给定一个数组 a a a,进行以下 q q q次操作,分两种。
- 1 x x x y y y,在 x x x后面插入 y y y。
- 2 x x x,将 x x x删去。
保证每次操作后,各个数互不相同。
经过
q
q
q次操作后,输出最后的数组
a
a
a
解题思路:
模拟即可。用数组模拟双链表,记录每一个点的左边元素是哪个,右边元素是哪个,同时记录头结点。由于 x , y x,y x,y的范围都比较大,可以用 m a p map map存储。最后遍历链表即可。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+100,mod=998244353;
typedef long long ll;
typedef pair<pair<int,int>,int> PII;
int T;
int n;
int a[N];
map<int,int> l;
map<int,int> r;
int h=-1;
void solve()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
h=a[1];
for(int i=1;i<=n;i++)
{
if(i==1) l[a[1]]=-1,r[a[1]]=a[2];
if(i==n) l[a[n]]=a[n-1],r[a[n]]=-1;
else {
l[a[i]]=a[i-1];
r[a[i]]=a[i+1];
}
}
int q;
cin>>q;
while(q--)
{
int id;
cin>>id;
if(id==1)
{
int x,y;
cin>>x>>y;
if(r[x]!=-1)
{
l[r[x]]=y;
r[y]=r[x];
r[x]=y;
l[y]=x;
}
else
{
r[x]=y;
l[y]=x;
r[y]=-1;
}
}
else
{
int x;
cin>>x;
if(h==x)
{
h=r[x];
l[r[x]]=-1;
}
else if(r[x]==-1)
{
r[l[x]]=-1;
}
else
{
r[l[x]]=r[x];
l[r[x]]=l[x];
}
}
}
for(int i=h;i!=-1;i=r[i])
{
cout<<i<<" ";
}
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
M - Earn to Advance
太难了
题意:
有一个 N × N N \times N N×N 的网格,高桥最开始在位置 ( 1 , 1 ) (1,1) (1,1),有 0 0 0 个金币。
假设高桥现在处于位置
(
i
,
j
)
(i,j)
(i,j),每一秒他
可以做出如下选择中的一种:
- 不移动,获得 P i , j P_{i,j} Pi,j 的金币。
- 移动到 ( i , j + 1 ) (i,j+1) (i,j+1),花费 R i , j R_{i,j} Ri,j 的金币。
- 移动到 ( i + 1 , j ) (i+1,j) (i+1,j),花费 D i , j D_{i,j} Di,j 的金币。
高桥在任意位置的金币数都不能小于 0 0 0。
求高桥到达 ( N , N ) (N,N) (N,N) 的最小时间。
样例输入 #1
3
1 2 3
3 1 2
2 1 1
1 2
4 3
4 2
1 5 7
5 3 3
样例输出 #1
8
样例 #2
样例输入 #2
3
1 1 1
1 1 1
1 1 1
1000000000 1000000000
1000000000 1000000000
1000000000 1000000000
1000000000 1000000000 1000000000
1000000000 1000000000 1000000000
样例输出 #2
4000000004
解题思路:
在转移时考虑前一个点
(
k
,
l
)
(k, l)
(k,l),其中满足
p
k
,
l
≤
p
i
,
j
p_{k, l} \leq p_{i, j}
pk,l≤pi,j。两点间的最小移动代价可以事先通过
O
(
8
0
4
)
O(80^4)
O(804) 预处理得到。然后在
(
k
,
l
)
(k, l)
(k,l) 充够钱后到达
(
i
,
j
)
(i, j)
(i,j)。
因此,到达点
(
i
,
j
)
(i, j)
(i,j) 的方式有好多种,不同的方式有不同的最小步数和当前钱数。要保留哪个呢?哪个是最优的呢?可以观察到,我们保留步数最小的,如果步数相同,则保留钱数最多的,这样转移一定最优的。
通过
O
(
8
0
4
)
O(80^4)
O(804) 的预处理,计算出所有点对之间的最小移动代价。
- 对于每个点 ( i , j ) (i, j) (i,j),遍历所有满足 p k , l ≤ p i , j p_{k, l} \leq p_{i, j} pk,l≤pi,j 的点 ( k , l ) (k, l) (k,l)。
- 计算从 ( k , l ) (k, l) (k,l) 到 ( i , j ) (i, j) (i,j) 的步数和钱数。
- 保留步数最小的转移方式,如果步数相同,则保留钱数最多的转移方式。
通过这种方式,可以确保在每个点 ( i , j ) (i, j) (i,j) 处选择最优的转移方式,从而在整个路径上达到最优解。
符号解释
- h i , j h_{i,j} hi,j:从 ( 1 , 1 ) (1,1) (1,1) 走到 ( i , j ) (i,j) (i,j) 所需的最小步数。
- q i , j q_{i,j} qi,j:从 ( 1 , 1 ) (1,1) (1,1) 走到 ( i , j ) (i,j) (i,j) 步数的最小情况下剩余最多钱的数量。
- g i , j , k , l g_{i,j,k,l} gi,j,k,l:从 ( k , l ) (k,l) (k,l) 到 ( i , j ) (i,j) (i,j) 需要最少钱的数量(需要保证 k ≤ i , l ≤ j k \leq i, l \leq j k≤i,l≤j)。
- P k , l P_{k,l} Pk,l:在点 ( k , l ) (k,l) (k,l) 的某种成本或费用。
实现思路
- 初始化 h h h 和 q q q 数组。
- 使用动态规划的方法,从 ( 1 , 1 ) (1,1) (1,1) 开始逐步计算每个点的 h h h 和 q q q 值。
- 在计算过程中,使用上述转移方程来更新 h h h 值。
- 最终得到 h n , n h_{n,n} hn,n 即为所求。
转移方程解释
h i , j = min k ≤ i , l ≤ j ( h k , l + ⌈ max ( g i , j , k , l − q k , l , 0 ) P k , l ⌉ + ( i − k ) + ( j − l ) ) h_{i,j} = \min_{k \leq i, l \leq j} \left( h_{k,l} + \left\lceil \frac{\max(g_{i,j,k,l} - q_{k,l}, 0)}{P_{k,l}} \right\rceil + (i - k) + (j - l) \right) hi,j=mink≤i,l≤j(hk,l+⌈Pk,lmax(gi,j,k,l−qk,l,0)⌉+(i−k)+(j−l))
这个方程的含义是从 ( 1 , 1 ) (1,1) (1,1) 到 ( i , j ) (i,j) (i,j) 的最小步数 h i , j h_{i,j} hi,j 可以通过以下方式计算:
- 遍历所有可能的前一个点 ( k , l ) (k,l) (k,l),其中 k ≤ i k \leq i k≤i 且 l ≤ j l \leq j l≤j。
- 对于每个前一个点 ( k , l ) (k,l) (k,l),计算从 ( k , l ) (k,l) (k,l) 到 ( i , j ) (i,j) (i,j) 的额外步数 ( i − k ) + ( j − l ) (i - k) + (j - l) (i−k)+(j−l)。
- 计算从 ( k , l ) (k,l) (k,l) 到 ( i , j ) (i,j) (i,j) 所需的额外费用,并将其转换为步数。这个额外费用是 g i , j , k , l − q k , l g_{i,j,k,l} - q_{k,l} gi,j,k,l−qk,l,如果这个值为负,则取 0。
- 将这个额外费用除以 P k , l P_{k,l} Pk,l 并向上取整,得到需要的额外步数。
- 将所有这些值相加,并取最小值作为 h i , j h_{i,j} hi,j。
最终答案
最终的答案是从 ( 1 , 1 ) (1,1) (1,1) 到 ( n , n ) (n,n) (n,n) 的最小步数,即 h n , n h_{n,n} hn,n。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+100,mod=998244353;
typedef long long ll;
typedef pair<pair<int,int>,int> PII;
int T;
int n;
const int M=82;
int p[M][M];
int r[M][M];
int d[M][M];
int dist[M][M][M][M];
int ans[M][M];
int q[M][M];
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>p[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<n;j++)
cin>>r[i][j];
for(int i=1;i<n;i++)
for(int j=1;j<=n;j++)
cin>>d[i][j];
memset(dist,0x3f,sizeof dist);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
dist[i][j][i][j]=0;
for(int k=i;k>=1;k--)
{
for(int l=j;l>=1;l--)
{
if(k==i&&l==j) continue;
dist[i][j][k][l]=min(dist[i][j][k+1][l]+d[k][l],dist[i][j][k][l+1]+r[k][l]);
}
}
}
}
memset(ans,0x3f,sizeof ans);
ans[1][1]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
for(int k=1;k<=i;k++)
{
for(int l=1;l<=j;l++)
{
int t=max((dist[i][j][k][l]-q[k][l]+p[k][l]-1)/p[k][l],0ll);
if(ans[k][l]+t+i-k+j-l<ans[i][j])
{
ans[i][j]=ans[k][l]+t+i-k+j-l;
q[i][j]=q[k][l]+t*p[k][l]-dist[i][j][k][l];
}
else if(ans[k][l]+t+i-k+j-l==ans[i][j]&&q[i][j]<q[k][l]+t*p[k][l]-dist[i][j][k][l])
{
q[i][j]=q[k][l]+t*p[k][l]-dist[i][j][k][l];
}
}
}
}
}
cout<<ans[n][n]<<endl;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
N - Points and Comparison
不会