https://codeforces.com/contest/1566
A
- 位置可以用 0 0 0占,所以显然是他前面都是 0 0 0
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <map>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 100;
const int INF = 0x3f3f3f3f;
int Data[MAXN];
int main(){
#ifdef LOCAL
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
ios::sync_with_stdio(false);
int t, n, s;
cin >> t;
while(t--){
cin >> n >> s;
int m;
if(n & 1){
m = n / 2;
}else{
m = n / 2 - 1;
}
m = n - m;
cout << s / m << '\n';
}
return 0;
}
B
给你一个 01 01 01串,将他剪开成若干段,取每一段的 M E X MEX MEX,问什么时候最小
- 显然应该把 0 0 0尽可能的连在一块,这样对答案的贡献最小,所以就是求有多少个 0 0 0的区域,如果这个值比 2 2 2还要大,那么就不用剪开了,整个一块即可
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <map>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 100;
const int INF = 0x3f3f3f3f;
int Data[MAXN];
int main(){
#ifdef LOCAL
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
ios::sync_with_stdio(false);
int t;
string s;
cin >> t;
while(t--){
cin >> s;
int len = s.length();
int sum = 0;
vector<int> vs;
for(int i=0;i<len;i++){
if(s[i] == '0'){
sum++;
vs.push_back(i);
}
}
if(sum == len) cout << 1 << '\n';
else if(sum == 0) cout << 0 << '\n';
else{
int num = 1;
for(int i=1;i<vs.size();i++){
if(vs[i] - 1 != vs[i - 1]) num++;
}
cout << min(2, num) << '\n';
}
}
return 0;
}
C
变成了两个串,这回问什么时候最大
- 如果上下不相等,那么显然不用动,这对答案的贡献是最大的,如果上下都是 1 1 1,那么现在这个贡献是 0 0 0,考虑它的右侧是否存在 0 0 0,不用考虑左面因为这个子问题刚才已经解决了,如果存在 0 0 0,那么这两块对答案的贡献就是 2 2 2,否则贡献就是 0 0 0,同理看上下都是 0 0 0的情况,如果右侧有 1 1 1,那么贡献是 2 2 2,这两块都跳过,否则贡献就是 1 1 1
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <map>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 100;
const int INF = 0x3f3f3f3f;
int Data[MAXN];
int main(){
#ifdef LOCAL
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
ios::sync_with_stdio(false);
int t, n;
string s1, s2;
cin >> t;
while(t--){
cin >> n;
cin >> s1 >> s2;
int ans = 0;
for(int i=0;i<n;){
if(s1[i] == '0' && s2[i] == '0'){
if(i < n && s1[i + 1] == '1' && s2[i + 1] == '1'){
ans += 2;
i += 2;
}else{
ans += 1;
i += 1;
}
}else if(s1[i] == '1' && s2[i] == '1'){
if(i < n && s1[i + 1] == '0' && s2[i + 1] == '0'){
ans += 2;
i += 2;
}else{
i += 1;
}
}else{
ans += 2;
i += 1;
}
}
cout << ans << '\n';
}
return 0;
}
D
给出 n × m n\times m n×m个人的视力值,规定值小的要坐在座位的左面,现在座位是 n n n行 m m m列的,从第一个人开始入座,也是从左往右走,如果他经过了其他人,那么不方便程度加一,问最小的不方便程度是多少
- 考虑贪心,如果两个人在同一行,如果编号小的人的视力值小,那么他显然应该坐在外面, 这样他对于编号大的人对于答案的贡献就是 1 1 1,所以此题的 e a s y easy easy版本可以此思路解决,那么如果行数大于 1 1 1,如果我们能够确定每一行都有谁,那么问题是不是就解决了呢?
- 先将视力值从小到大排序,然后视力值相同的按照编号从小到大排序,这一过程就确定了每一行的人,为什么?因为现在要从前往后放人,如果存在视力值小的,那他一定要尽量往前放,如果往后放,他必然要为视力值大的人贡献答案,这是我们不想看到的,如果有两个人视力值相等,那显然应该先放编号大的,因为这样才能不给编号小的贡献答案
- 接下来就是给每一行放人,这个时候,如果编号小的视力值小,那他就会贡献答案,也就是找编号和视力值之间有多少正序对
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 100;
const double eps = 1e-6;
int a[MAXN];
int main(){
#ifdef LOCAL
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t, n, m;
cin >> t;
while(t--){
cin >> n >> m;
vector<pair<int, int> >PII (n * m);
for(int i=0;i<n*m;i++){
cin >> PII[i].first;
PII[i].second = i;
}
sort(PII.begin(), PII.end());
for(int i=0;i<n*m;i++){
PII[i].second = -PII[i].second;
}
int ans = 0;
for(int i=0;i<n;i++){
sort(PII.begin() + i * m, PII.begin() + i * m + m);
for(int j=0;j<m;j++){
for(int k=0;k<j;k++){
if(PII[i * m + j].second < PII[i * m + k].second) ans++;//这里由于变了负号,大小关系要反过来
}
}
}
cout << ans << '\n';
}
return 0;
}
E
一开始题目理解错了,把芽定义弄错了,但是这道题其实是比较容易的
- 芽指的是不是根节点也不是叶子节点且它的孩子节点都是叶子节点的节点,注意它的孩子必须是叶子节点
- 现在可以进行若干次操作,每次操作都可以把芽及其后代移动到另一个节点上去,当然要保证得到的仍然是一棵树,问移动完剩下的最少叶子节点数
思路 - 如果根节点的孩子节点有叶子节点,那么只需要把芽移动到这个位置,然后剩余的芽接到下面那个芽上面,如果设芽的个数为 k k k,总的节点数为 n n n,那么显然叶子节点总数为 n − k − 1 n-k-1 n−k−1,每一次的移动都会使叶子节点的个数减少 1 1 1,所以一共减少的叶子节点数就是 k k k,那么最终的答案就是 n − 2 × k − 1 n-2\times k-1 n−2×k−1
- 如果根节点的孩子节点没有叶子节点,那么就需要把芽移动到它的孩子芽上面去,那么这样就会减少一个芽的贡献,也就是一共减少的叶子节点数量是 k − 1 k-1 k−1,所以最终的答案就是 n − 2 × k n-2\times k n−2×k
- 所以只需要统计所有节点到底是叶子节点还是芽即可
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdio>
#include <map>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 4e5 + 100;
const int INF = 0x3f3f3f3f;
int Data[MAXN];
int head[MAXN];
int type[MAXN];
struct Edge{
int next;
int to;
int val;
}edge[MAXN];
int cnt;
void Add_Edge(int u, int v, int w){
edge[cnt].next = head[u];
edge[cnt].to = v;
edge[cnt].val = w;
head[u] = cnt++;
}
void dfs(int u, int fa){
bool leave = false;
for(int i=head[u];~i;i=edge[i].next){
int v = edge[i].to;
if(v == fa) continue;
dfs(v, u);
if(type[v] == 1){
leave = true;
}
}
if(!leave){
type[u] = 1;
}else{
type[u] = 2;
}
}
int main(){
#ifdef LOCAL
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
ios::sync_with_stdio(false);
int t, n, u, v;
cin >> t;
while(t--){
cnt = 0;
memset(head, -1, sizeof head);
memset(type, -1, sizeof type);
cin >> n;
for(int i=1;i<n;i++){
cin >> u >> v;
Add_Edge(u, v, 1);
Add_Edge(v, u, 1);
}
dfs(1, -1);
int has_leave = 0;
for(int i=head[1];~i;i=edge[i].next){
int v = edge[i].to;
if(type[v] == 1){
has_leave = 1;
}
}
int k = 0;
for(int i=2;i<=n;i++){
k += type[i] == 2;
}
cout << n - 2 * k - has_leave << '\n';
}
return 0;
}