A. Musical Puzzle
题意:
给一个字符串,每两个相邻的字符组成一个块,求有多少个不同的块。
思路:
利用set去重,输出set的大小即可
代码:
int n;
string str;
set<string> s;
void solve() {
cin >> n >> str;
str = " " + str;
s.clear();
for (int i = 2; i <= n; i++) {
char s1 = str[i - 1], s2 = str[i];
string s3 = "";
s3 += s1;
s3 += s2;
s.insert(s3);
}
cout << s.size() << endl;
}
B. Restore the Weather
题意:
给出a,b两个数组和k,现在要重新排列b,使得|a[i] - b[i]| <= k,输出重新排列的k。
思路:
因为题目保证有解,所以只要最小的a对应最小的b,第二小对应…即可。可以将a,b排序,然后按照大小一一映射。
代码:
int n, k;
int a[maxn], b[maxn], id1[maxn], id2[maxn];
bool cmp(int i, int j) {
return a[i] < a[j];
}
bool cmp2(int i, int j) {
return b[i] < b[j];
}
void solve() {
cin >> n >> k;
for (int i = 1; i <= n; i++) cin >> a[i], id1[i] = i;
for (int i = 1; i <= n; i++) cin >> b[i], id2[i] = i;
sort(id1 + 1, id1 + n + 1, cmp);
sort(id2 + 1, id2 + n + 1, cmp2);
for (int i = 1; i <= n; i++) {
a[id1[i]] = b[id2[i]];
}
for (int i = 1; i <= n; i++) cout << a[i] << ' ';
cout << endl;
}
C. Vlad Building Beautiful Array
题意:
给出一个数组a,a[i]可以-=a[j],j随便取,问a数组能不能变成全部元素大于0且都是奇数或都是偶数,可以输出YES,不能输出NO。
思路:
偶数可以减奇数,变成奇数,所以只要最小的偶数大于最小的奇数即可,因为奇数只能减奇数变成偶数,所以最小的奇数无法变成偶数,所以机速变偶数是行不通的,最后特判一下全是奇数或全是偶数即可。
代码:
void solve() {
int miod = 0x3f3f3f3f, miev = 0x3f3f3f3f;
cin >> n;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
if (x & 1) miod = min(x, miod);
else
{
miev = min(x, miev);
}
}
if (miev > miod || miod == 0x3f3f3f3f || miev == 0x3f3f3f3f) cout << "YES" << endl;
else cout << "NO" << endl;
}
D. Flipper
题意:
给一个排列,要求做一次操作,选择一个区间,翻转区间中的数,同时区间前面的数全部挪到后面去,区间后面的数全部挪到前面去。求操作后的最大字典序,输出最大字典序的排列,n <= 2000。
思路:
因为n <= 2000 ,所以可以暴力,因为字典序要最大,所以得找到最大的数,把它放到最前面,因此,选择的区间右端点r得固定在最大数前面,因为必须要进行一次操作,所以最大数因该从i==2开始找,找到右端点后,枚举左端点l即可。具体看代码。
代码:
int n, a[maxn];
string get(int x) {
string ans;
while (x) {
ans += (x % 10 + '0');
x /= 10;
}
//ans.reserve();
string res;
for (int i = ans.size() - 1; i >= 0; i--) res += ans[i];
return res;
}
string mymax(string a, string b) {
if (a.size() > b.size()) return a;
if (a.size() < b.size()) return b;
string tem, temb;
for (int i = 0, j = 0; i < a.size() && j < b.size(); i++, j ++ ) {
tem = "";
while (a[i] != ' ') tem += a[i], i++;
temb = "";
while (b[j] != ' ') temb += b[j], j++;
if (tem.size() > temb.size()) return a;
if (tem.size() < temb.size()) return b;
if (tem > temb) return a;
if (tem < temb) return b;
}
if (tem.size() > temb.size()) return a;
if (tem.size() < temb.size()) return b;
if (tem > temb) return a;
if (tem < temb) return b;
return a;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
string ans;
int r = n + 1;
int mx = 0;
for (int i = 2; i <= n; i++) {
if (a[i] > mx) {
mx = a[i];
r = i;
}
}
if (r == n) {
ans += get(a[n]) + " ";
for (int i = 1; i <= n - 1; i++) ans += get(a[i]) + " ";
}
if (r == 2) {
string tem;
for (int i = 2; i <= n; i++) tem += get(a[i]) + " ";
tem += get(a[1]) + " ";
ans = mymax(ans, tem);
}
r--;
for (int l = 1; l <= r; l++) {
string tem;
for (int i = r + 1; i <= n; i++) tem += get(a[i]) + " ";
for (int i = r; i >= l; i--) tem += get(a[i]) + " ";
for (int i = 1; i < l; i++) tem += get(a[i]) + " ";
ans = mymax(ans, tem);
}
cout << ans << endl;
}
E. Round Dance
题意:
现在有n个人,每个人记得一个邻居,每个人可以找两个人跳舞,跳舞最少要两个人,每个人都要跳舞,首先他要去找他记得的邻居a[i],然后还可以再选一个人连起来。问最少和最多可以有多少组舞蹈。
思路:
首先用并查集将a[i]和i连起来,然后每个联通块都是一组舞蹈,最多数量就是联通块的数量,因为每个人可以找两个人组合,所以环是无法再拉入人的,因为每个人都有两条边,而当一个联通块里面有链,它就可以与其他的包含链的联通块组合起来。所以最小数量就是成环的联通块,如果有存在链的联通块,就再加一,因为链可以合并起来。至于找链,只要a[a[i]] == i,那么这个联通块里面就有链,因为一个环的话每个人都会和两个不同的人连边,如果存在两个人连了两条边,就一定不存在环。
代码
int n, a[maxn];
int p[maxn];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
void merge(int x, int y) {
if (find(x) == find(y)) return;
p[find(x)] = find(y);
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) p[i] = i;
for (int i = 1; i <= n; i++)
merge(i, a[i]);
int mi = 0, mx = 0;
int chain = 0;
vector<int> e(n + 1, 0);
for(int i = 1; i <= n; i ++ )
if (a[a[i]] == i) {
e[find(i)] = 1;
chain = 1;
}
for (int i = 1; i <= n; i++) {
if (find(i) == i) {
if (!e[i]) mi++;
mx++;
}
}
cout << mi + chain << ' ' << mx << endl;
}
F. Ira and Flamenco
题意:
给一个数组,你要选m个数,且其中最大-最小要小于m,求方案数mod1e9+7。
思路:
先记录每个数出现的次数,再用set给数组排序去重一下。对于合法的m个数,其选择方案数就是每个数的数量乘起来。然后枚举每个合法区间即可,求区间方案数,可以用前缀和来求,具体看代码。
代码:
int n, a[maxn];
int m;
int qmi(int a, int b){
int ans = 1;
while(b){
if(b & 1) ans = (ans * a) % mod;
b >>= 1;
a = (a * a) % mod;
}
return ans;
}
void solve(){
cin >> n >> m;
map<int, int> mp;
set<int> s;
for(int i = 1; i <= n; i ++ ){
cin >> a[i];
mp[a[i]] ++ ;
s.insert(a[i]);
}
n = s.size();
vector<int> num(n + 1);
num[0] = 1;
int cnt = 0;
vector<int> b(n + 1);
for(auto i : s){
cnt++;
b[cnt] = i;
num[cnt] = num[cnt - 1] * mp[i] % mod;
}
int ans = 0;
for(int i = m; i <= n; i ++ )
if(b[i] - b[i - m + 1] < m)
ans = (ans + num[i] * qmi(num[i - m], mod - 2) % mod) % mod;
cout << ans << endl;
}
G. Ksyusha and Chinchilla
题意:
给一颗树,你可以删除其中的边,问你能否通过删除边,使得这颗树变成若干个由三个点组成的块,如果可以输出删除边的编号,如果不可以输出-1.
思路:
深搜,返回的时候每遇到三个点就切一刀,如果返回的时候遇到大于3个点了,那就直接输出-1,像这种
.如果最后返回到根节点,点数不够3,也输出-1,
像这种
代码:
int n;
vector<pair<int, int>> g[maxn];
int d[maxn];
vector<int> ans;
int ok = 1;
void dfs(int u, int f, int id){
if(!ok) return;
d[u] = 1;
for(auto tem : g[u]){
int x = tem.first, y = tem.second;
if(x == f) continue;
dfs(x, u, y);
d[u] += d[x];
}
if(d[u] > 3) ok = 0;
if(d[u] == 3){
d[u] = 0;
ans.push_back(id);
}
}
void solve(){
cin >> n;
for(int i = 1; i <= n; i ++ ) g[i].clear();
for(int i = 1; i <= n - 1; i ++ ){
int u, v;
cin >> u >> v;
g[u].push_back({v, i});
g[v].push_back({u, i});
}
ok = 1;
ans.clear();
dfs(1, 1, 0);
if(!ok || d[1]) cout << -1 << endl;
else{
ans.pop_back();
cout << ans.size() << endl;
for(auto i : ans) cout << i << ' ';
cout << endl;
}
}