前言
bilibili牛客竞赛官方视频讲解
- 更为详细
正式内容
C - 连锁进位
思路:
- 简单的模拟
- 从后往前,每次加某个数使得该为变为0即可
- 题目中
n>1
所以可以忽略n == 0
的情况
第一种写法,建议跳过(太差)
以下是代码部分——本弱智写的代码(缺点:需要多次特判)
#include<bits/stdc++.h>
using namespace std;
void solve()
{
string s;
cin >> s;
int ans = 0;
int pos = (int)s.size() - 1;
//查找第一个非零的位置,且不为最大的一位
while(s[pos] == '0' && pos >= 1) pos --;
//如果第一个非零的位置不是最高位
if(pos != 0) ans = 10 - s[pos] + '0';
//如果是最高位,或者最高位为0
else
{
//如果最高位为0
if(s[pos] == '0')
cout << "1\n";
//如果最高位不是0
else
cout << "0\n";
return ;
}
//该位置判断过了,判断下一个
pos --;
for(int i = pos; i >= 1; i --)
{
//注意9的进位即可
if(s[i] != '9')
ans += 9 - s[i] + '0';
}
cout << ans << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while(t --)
solve();
return 0;
}
第二种写法
以下是代码部分,代码参考来源——蒟蒻炖蒟蒻
#include<bits/stdc++.h>
using namespace std;
string s;
void solve()
{
cin >> s;
int n = (int)s.size();
//ans存储结果,jw存储是否需要进位
int ans = 0, jw = 0;
for(int i = n - 1; i > 0; i --)
{
//temp代表这一位上的数
int temp = s[i] - '0' + jw;
//如果temp不为0
if(temp)
{
//加上结果
ans += 10 - temp;
//并且需要进位
jw = 1;
}
//如果无需进位
else jw = 0;
}
cout << ans << '\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while(t --)
solve();
return 0;
}
D - 因子区间
思路:
1e5
的数据,其因子数很小。利用这个性质,可以做以下前缀和dp[i][j]
表示从1~i
区间中,因子数为j
的数量- 之后通过等差数列求和公式,求出答案。
以下是代码部分
在这里#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
int a[N];
ll dp[N][170];
void solve()
{
int n, q;
cin >> n >> q;
for(int i = 1; i <= n; i ++)
cin >> a[i];
for(int i = 1; i <= n; i ++)
{
int cnt = 0;
//求因子数目
for(int j = 1; j * j <= a[i]; j ++)
{
if(a[i] % j == 0)
{
cnt ++;
if(a[i] / j != j) cnt ++;
}
}
a[i] = cnt;
}
//先赋值
for(int i = 1; i <= n; i ++)
dp[i][a[i]] = 1;
//前缀和的操作
for(int i = 1; i < 170; i ++)
for(int j = 2; j <= n; j ++)
dp[j][i] += dp[j - 1][i];
while(q --)
{
int l, r;
cin >> l >> r;
//记录答案
ll ans = 0;
for(int i = 1; i < 170; i ++)
{
//前缀和
ll temp = dp[r][i] - dp[l - 1][i];
ans += temp * (temp - 1) / 2;
}
cout << ans << '\n';
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int t = 1;
//cin >> t;
while(t --)
solve();
return 0;
}
E - 小苯的排列构造
思路:
1: 5 7 9 11 ……
2: 6 8 10 12……
3: 7 9 11 13……
4: 8 10 12 14……
5: 1 9 11 13……
6: 2 10 12 14 ……
7: 3 11 13 15 ……
8: 4 12 14 16 ……
9: 5 13 15……
10: 6 14 16 ……
- 得出归律:至少要8个数才能满足要求。大于8个数时,每4个数交换一下位置。
- 比如:当
n == 10
时 5 6 7 8 1 2 3 4 9 10 ==> 5 6 7 8 9 10 3 4 1 2
以下是代码部分
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
int a[N] = {0, 5, 6, 7, 8, 1, 2, 3, 4};
void solve()
{
int n;
cin >> n;
if(n <= 7)
{
cout << "-1\n";
return ;
}
for(int i = 9; i <= n; i ++)
{
a[i] = i;
swap(a[i], a[i - 4]);
}
for(int i = 1; i <= n; i ++)
cout << a[i] << " \n"[i == n];
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int t = 1;
//cin >> t;
while(t --)
solve();
return 0;
}
F - 小红的基环树删边
思路:
- 来源题解——牛客288141082号的题解
- 因为为基环树,所以从结点1到结点n的路径最多只有两条。
- 每次遍历其中一条路径:
- 那另一条没被走到的路径上的任一条边断开都是另一条路径的长度。
- 如果
f[i]
始终为无穷,则代表第i
条边断开,就无法从1结点走到n结点 - 记得取最小值。
以下是代码部分,代码参考来源——牛客288141082号
- 代码为他人代码,这里只是稍加解释
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
//存储边的起点,终点和编号
vector<pair<int, int> > v[N];
int f[N];
bool vis[N];
int n;
//深搜
void dfs(int x, int dep)
{
//停止标志,代表已走到终点n
if(x == n)
{
//给没有被遍历到的结点赋值
for(int i = 1; i <= n; i ++)
if(!vis[i])
f[i] = min(f[i], dep);
return ;
}
for(auto[y, id] : v[x])
if(!vis[id])
{
//标记为已走过该结点
vis[id] = true;
//路径长度+1
dfs(y, dep + 1);
//回溯
vis[id] = false;
}
}
void solve()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
int x, y;
//初始化f[],赋值为无穷
f[i] = 1e9;
cin >> x >> y;
//存入边(为无向图,存入两条)
v[x].emplace_back(y, i);
v[y].emplace_back(x, i);
}
dfs(1, 0);
//输出
for(int i = 1; i <= n; i ++)
{
//无法到达,输出-1
if(f[i] > 1e6) f[i] = -1;
cout << f[i] << '\n';
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int t = 1;
//cin >> t;
while(t --)
solve();
return 0;
}