Codeforces #479 (div 3)
Codeforces #479 (div 3)
A. Wrong Subtraction
题意: 对一个数 x ,可以执行如下操作:
- 最后一位非零,\(x= x - 1\)
- 最后一位为0,\(x = x / 10\)
问执行k次操作后的值是多少。
题解: 模拟执行操作的过程。
#include<bits/stdc++.h>
using namespace std;
int n, k;
int main()
{
scanf("%d%d",&n,&k);
while(k --){
if(n % 10 == 0) n /= 10;
else n -= 1;
}
printf("%d\n",n);
return 0;
}
B. Two-gram
题意:给你一个字符串(仅含A-Z),将相邻两个字符的组合称为two-grams
, 求出现次数最多的 two-grams
。
题解: 对每个字符组合进行哈希,考虑到组合数并不多,可以开一个num数组记录每个组合出现次数。在更新次数最大值maxx时顺便更新一下答案第二个字符的位置pos。
#include<bits/stdc++.h>
using namespace std;
const int N = 3e6 + 105;
int n, pos, num[N];
char s[N];
int main()
{
scanf("%d",&n);
scanf("%s",s + 1);
int maxx = 0;
for(int i = 2; i <= n; ++ i){
int tp = ++ num[(s[i - 1] - 'A') * 30 + (s[i] - 'A')];
if(tp > maxx){
maxx = tp;
pos = i;
}
}
printf("%c%c\n",s[pos - 1], s[pos]);
return 0;
}
C. Less or Equal
题意: 给一个大小为n的序列和一个数 k, 求一个数 x使得该序列中小于等于x的数的个数恰好为k。
题解: 将序列从小到大排序后,如果a[k] != a[k + 1] 就输出 a[k],否则输出 -1。注意特判 k = 0的情况:a[1] > 1时输出1,否则输出 -1。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 5;
int n, k;
ll a[N];
int main()
{
scanf("%d%d",&n,&k);
for(int i = 1; i <= n; ++ i) scanf("%lld",&a[i]);
sort(a + 1, a + n + 1);
if(k == 0){
if(a[1] > 1) printf("1\n");
else printf("-1\n");
return 0;
}
if(a[k] != a[k + 1]) printf("%lld\n",a[k]);
else printf("-1\n");
return 0;
}
D. Divide by three, multiply by two
题意: 给大小为n的数组,要求重排这个数组,使得满足如下要求:如果第 i个数为 \(x\) ,那第 i + 1 个数为 \(2x\) 或 \(x / 3\) (x % 3 == 0时)。保证一定有解。
题解:
思路1:搜索,因为一定有解,所以可以固定第1个数,然后搜后面的数。
思路2:to[i]表示第 i 数后面放的数的位置,vis[i]表示第 i 个数前面的值有没有确定。从小到大排序,对每个数x,如果没有数在它前面,说明x/2没有在x前面,那只能试着把3x放在x前面,然后如果没有确定x后面的数,就试着把2x放在x后面。不考虑放x/3是因为x/3已经处理过了,肯定有放在它前面的值了。最后找一个前面没有值的数开始,沿着to[]数组的转移输出重排的序列。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5 + 5;
int n;
ll a[N];
map<ll, int> mp;
int to[N], vis[N];
vector<int> res;
int main()
{
scanf("%d",&n);
for(int i = 1; i <= n; ++ i) {
scanf("%lld",&a[i]);
}
sort(a + 1, a + n + 1);
for(int i = 1; i <= n; ++ i) mp[a[i]] = i;
for(int i = 1; i <= n; ++ i){
if(!vis[i]){
ll tp = a[i] * 3;
if(mp[tp]) {
to[mp[tp]] = i;
vis[i] = 1;
}
}
if(!to[i]){
ll tp = a[i] * 2;
if(mp[tp]){
to[i] = mp[tp];
vis[mp[tp]] = 1;
}
}
}
for(int i = 1; i <= n; ++ i){
if(vis[i]) continue;
int tp = i;
while(tp){
res.push_back(tp);
tp = to[tp];
}
break;
}
int tt = res.size();
for(int i = 0; i < tt; ++ i){
printf("%lld",a[res[i]]);
if(i == tt - 1) printf("\n");
else printf(" ");
}
return 0;
}
E. Cyclic Components
题意: 给n个点, m条边,求如下图这样环的个数。
题解: 可以发现,每个这样的环首先是一个联通块,而且联通块中每个点的度数为2。根据这一点我们用并查集找到所有联通块,如果一个联通块中度数都为2,res ++。最后res就是满足题意的环的个数。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 105;
int n, m, res;
int root[N], in[N], vis[N];
int Find(int x){
return x == root[x] ? x : root[x] = Find(root[x]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 0; i <= n; ++ i) root[i] = i;
for(int i = 1; i <= m; ++ i){
int x, y; scanf("%d%d",&x,&y);
in[x] ++, in[y] ++;
int tx = Find(x), ty = Find(y);
if(tx != ty) root[tx] = ty;
}
for(int i = 1; i <= n; ++ i){
if(in[i] != 2) vis[Find(i)] = 1;
}
for(int i = 1; i <= n; ++ i){
if(root[i] == i && !vis[i]) res ++;
}
printf("%d\n",res);
}
F. Consecutive Subsequence
题意: 给一个序列,求最长连续递增子序列。要求输出这个序列的长度和下标。
题解: dp转移很好想:\(dp[i] = max(dp[i], dp[mp[a[i] - 1]] + 1)\) , 就是a[i]用前面最近的a[i - 1]更新。用p[]数组记录每个位置最后被前面哪个位置更新。假设dp[pos]最大,那就可以从pos沿着p[]数组一直向前把这个序列还原。注意还原过程是从后往前的,所以输出时要逆序输出。
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 105;
int n;
map<int, int>mp;
int dp[N], p[N];
vector<int> res;
int main()
{
scanf("%d",&n);
for(int i = 1; i <= n; ++ i){
int x; scanf("%d",&x);
mp[x] = i;
if(dp[mp[x - 1]] + 1 > dp[i]){
dp[i] = dp[mp[x - 1]] + 1;
p[i] = mp[x - 1];
}
}
int maxx = 0, pos = 0;
for(int i = 1; i <= n; ++ i){
if(dp[i] > maxx){
maxx = dp[i], pos = i;
}
}
printf("%d\n",maxx);
while(pos){
res.push_back(pos);
pos = p[pos];
}
int tt = res.size();
for(int i = tt - 1; i >= 0; -- i){
printf("%d",res[i]);
if(i == 0) printf("\n");
else printf(" ");
}
return 0;
}