原题链接
我太菜了只会两道T_T
A. Shuffle Party
题目大意:给一个从 1 到 n 的排列
1
,
2
,
3
,
.
.
.
,
n
1,2,3,...,n
1,2,3,...,n 要求从 2 开始对每个数 k 找出其最大因数 x,并交换
a
x
a_x
ax 与
a
k
a_k
ak ,当结束操作后 1 所在的位置。
列出前几个,找规律:
1 2
2 1 3
2-2
3 1 2 4
3-2
3 4 2 1 5
4-4
5 4 2 1 3 6
5-4
5 4 6 1 3 2 7
6-4
7 4 6 1 3 2 5 8
7-4
7 4 6 8 3 2 5 1 9
8-8
不难发现 n 次操作后 1 一定在小于等于 n 的最大的
2
y
2^y
2y 的位置上
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ios::sync_with_stdio(false),cin.tie(0);
int t;
cin>>t;
while(t--) {
ll n,ans=1;
cin>>n;
while(1<<ans<=n) ans++;
ans--;
cout<<(1<<ans)<<'\n';
}
return 0;
}
B. Binary Path
题目大意:给定一个 2 行 n 列的矩阵,其中只包含 0 或 1,找出从左上角到右下角路径字符串字典序最小的路线,并指出这样的路线有几条。
由于题目要求路径字符串的字典序最小,那么当前不断选择 0 (如果能的话)一定是最优解,当我们已经走到第二行时,唯一的选择是不断向右走;当处于第一行时,假如当前位置的右边和下面都为 0 ,那么选择向右走一定比向下走更优,因为向下走之后就只有一种选择(向右走),而向右走仍然保持着两种选择(向右与向下)。
在得到答案路径的字符串 str 后,我们便可以根据这个字符串来维护到每个点(i、j)的路径数 dp[i][j]
- 首先计算第一行的 dp 的值,初始化起点的路径数
dp[0][0] = 1
从起点后面一个点开始向后遍历,如果第 j 列的字符等于 str 中对应位置时,dp[0][j] = 1
否则跳出循环 - 计算第二行,由于第二行可以从第一行下来,所以对于每列 j 如果与str中对应位置字符相等,那么
dp[1][j] = (dp[0][j] + dp[1][j - 1])
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[2][200010];
void solve()
{
int n;
cin>>n;
string mp[2];
cin>>mp[0]>>mp[1];
string ans="";
ans+=mp[0][0];
int x=0,y=0;
while(x!=1||y!=n-1) {
if(y==n-1) {
x++,ans+=mp[x][y];
continue;
}
if(x==0) {
if(mp[0][y+1]=='0') {
y++;
ans+='0';
} else if(mp[1][y]=='0') {
x++;
ans+='0';
} else {
y++;
ans+='1';
}
} else {
ans+=mp[x][++y];
}
}
cout<<ans<<'\n';
vector<vector<int>> dp(2,vector<int> (n));
dp[0][0]=1;
for(int i=1;i<n;i++) {
if(mp[0][i]==ans[i])
dp[0][i]=1;
else break;
}
for(int i=0;i<n;i++) {
if(mp[1][i]==ans[i+1]) {
dp[1][i]+=dp[0][i];
if(i-1>=0) dp[1][i]+=dp[1][i-1];
}
}
cout<<dp[1][n-1]<<'\n';
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0);
int t;
cin>>t;
while(t--) {
solve();
}
return 0;
}