AtCoder Beginner Contest 220
A
题目
略
思路
lue
AC代码
#include <bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define debug(x) cout<<"> "<< x<<endl;
#define endl '\n'
//#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N =10 + 1e5, mod = 1e9 + 7;
int a,b,c;
void solve()
{
cin>>a>>b>>c;
for(int i=a;i<=b;i++)
{
if(i%c == 0)
{
cout<<i<<endl;
return ;
}
}
cout<<-1<<endl;
return;
}
signed main()
{
ios::sync_with_stdio();cin.tie();cout.tie();
solve();
return 0;
}
B
题目
略
思路
略
AC代码
#include <bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define debug(x) cout << "> " << x << endl;
#define endl '\n'
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 10 + 1e5, mod = 1e9 + 7;
int base;
string str;
int a,b;
int turn()
{
int x=1;
int res =0;
int n = str.size();
for(int i=n-1;i>=0;i--)
{
res += x * (str[i] - '0');
x *= base;
}
return res;
}
void solve()
{
cin>>base;
cin>>str;
a = turn();
cin>>str;
b = turn();
cout<<a * b<<endl;
}
signed main()
{
ios::sync_with_stdio();
cin.tie();
cout.tie();
solve();
return 0;
}
C
题目
略
思路
略
AC代码
#include <bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define debug(x) cout<<"> "<< x<<endl;
#define endl '\n'
//#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N =10 + 1e5, mod = 1e9 + 7;
int n;
int a[N];
ll sum;
ll S;
void solve()
{
cin>>n;
for(int i=0;i<n;i++)cin>>a[i],S+=a[i];
cin>>sum;
ll cnt = sum / S * n;
sum = sum%S;
S = 0;
for(int i=0;i<n;i++){
cnt ++;
S += a[i];
if(S>sum)break;
}
cout << cnt << endl;
}
signed main()
{
ios::sync_with_stdio();cin.tie();cout.tie();
solve();
return 0;
}
D
题目
给一个序列,每次可以对最左边两个进行加或者乘的操作,取结果的个位。用这样的方法不断合并序列,直到它只剩一个元素,对于所有不同操作方案,统计0~9出现的个数。
思路
- 定义一个 d p dp dp数组,表示0~9出现的个数。
- 每次更新 d p dp dp,显然每次都是由上一个状态的的数字做乘数或者除数转移而来。这样扫一遍序列,就可以在线性的时间中解决这一问题。
AC代码
时间复杂度: O ( n ) O(n) O(n)
#include <bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define debug(x) cout<<"> "<< x<<endl;
#define endl '\n'
//#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N =10 + 1e5, mod = 998244353;
int n;
int a[N];
ll dp[2][10];
void solve()
{
cin>>n;
for(int i=0;i<n;i++)cin>>a[i];
dp[0][a[0]] = 1;// 初始化
int pre = 0, now = 1;
for(int i=1;i<n;i++){
for(int j = 0;j< 10 ;j++){
if(dp[pre][j]){// 如果上一状态有乘数(加数)
ll tmp = a[i] * j % 10;
dp[now][tmp] = (dp[now][tmp] + dp[pre][j]) % mod;
tmp = (a[i] + j) % 10;
dp[now][tmp] = (dp[now][tmp] + dp[pre][j]) % mod;
}
}
swap(now,pre);
for(int j =0 ;j<10;j++)dp[now][j] = 0;// 注意重置这一层,进行下一次的迭代
}
for(int i =0 ;i<10;i++) cout << dp[pre][i] << endl;
}
signed main()
{
ios::sync_with_stdio();cin.tie();cout.tie();
solve();
return 0;
}
E
题目
给出一个n层的完全二叉树,求最短距离为k的节点对的个数 , 输出对998244353取模的结果
思路
-
计数类问题 考虑dp或者推出计算公式
-
类似于对左子树和右子树的划分,以某点为“根节点”可以把链接两节点的路径划分为左路径和右路径向下延伸。
因此只要求出左右路径的个数的乘积再乘以所有可以成为根节点的节点个数即可。
-
细节较多,都在注释里
AC代码
时间复杂度: O ( n + k ) O(n + k) O(n+k)
#include <bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define debug(x) cout<<"> "<< x<<endl;
#define endl '\n'
//#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N =10 + 1e6, mod = 998244353;
int n , k;
int pw[N];// 2的次方
void solve()
{
cin >> n >> k;
pw[0] = 1;
for(int i = 1;i < N ;i++) (pw[i] = pw[i-1] * 2) %= mod;// 预处理二的幂
ll res = 0;
for(int l = 0 ; l <= k ; l ++){// 枚举左边路径长度
int r = k - l;// 右边路径长度
if(max(l,r) > n - 1) continue; // 如果最长的路径超出了可行范围 跳过这不合法情况
int tot = pw[n - max(l,r)] - 1;// 可行根节点个数 (记得减一)
res = (res + 2ll * tot % mod * pw[max(l - 1,0)] % mod * pw[max(r - 1,0)] % mod) %mod;
// 一来一回 * 可行根节点个数 * 左路径个数 * 右路径个数
// 注意爆int 注意当子路径长度为0时的特判
}
cout << res << endl;
}
signed main()
{
ios::sync_with_stdio();cin.tie();cout.tie();
solve();
return 0;
}
F
题目
求一棵树任意点到其他点的距离和。
思路
- 我们可以采用dp的思想解决这个问题
- 定义
d
p
[
i
]
dp[i]
dp[i]为以
i
i
i号节点为父亲节点,到其他各点的距离和。
将点分为从属于 i i i的儿子节点和其他节点,分两部分求解这个问题。- 儿子节点到该节点的距离和
- 其余节点到该节点的距离和
- 先求
i
i
i的子节点们到它的距离,只要从树的最底层向上转移,对任意一个子过程都是
d
p
[
j
]
+
s
i
z
e
[
j
]
dp[j] + size[j]
dp[j]+size[j]
j j j是父亲节点的儿子节点,向上迁移过程中对每个子节点到根节点的路径长度增加一,所以需要统计一个以子树的节点个数 s i z e size size. - 现在
d
p
dp
dp中记录了子节点到某点的距离和,现在更新其余点到它的距离和,对于统领整棵树的根节点来说,它的
d
p
dp
dp值已经更新完毕,因此我们应该从它出发,向其他节点转移状态。
考虑 d p dp dp定义,这部分的距离和应该是,该点的父节点的 d p dp dp值(已经被更新了,两种情况都包含了)减去该点的 d p dp dp值(还未更新,只有第一种情况),再加上其余点到该点多走那一步即为所求。
显然对于这个过程,从上往下更新即可。
AC代码
时间复杂度: O ( n ) O(n) O(n)
#include <bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define debug(x) cout<<"> "<< x<<endl;
#define endl '\n'
//#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N =10 + 2e5, mod = 1e9 + 7;
int n;
vector<int> tr[N];// 存树
ll dp[N];// 到i点的距离和
ll sz[N];// 子树节点个数
bool vis[N];// 标记数组
ll dfs(int rot = 1)// 更新sz 同时求情况一的dp值
{
ll res = 1;// 一开始有根节点提供的一个
for(auto v : tr[rot]){
if(vis[v]) continue;
vis[v] = true;
sz[v] = dfs(v);
res += sz[v];// 从下往上更新,所以先递归再更新
dp[rot] += dp[v] + sz[v];
}
return res;
}
void dfs2(int rot = 1)// 更新情况2的dp值
{
for(auto v : tr[rot]){
if(vis[v]) continue;
vis[v] = true;
dp[v] += dp[rot] - (dp[v] + sz[v]) + (n - sz[v]);// 从上往下更新所以先更新再递归
dfs2(v);
}
}
void solve()
{
scanf("%d",&n);
for(int i = 0 ;i < n - 1;i ++){
int a , b;
scanf("%d%d",&a,&b);
tr[a].push_back(b);
tr[b].push_back(a);// 建图
}
vis[1] = true;
sz[1] = dfs();
memset(vis,0, sizeof vis);
vis[1] = true;
dfs2();
for(int i = 1;i <= n; i ++) cout << dp[i] << endl;
}
signed main()
{
ios::sync_with_stdio();cin.tie();cout.tie();
solve();
return 0;
}