https://codeforces.com/contest/1594
A
我们显然可以构造出一个 [ − n + 1 , n ] [-n+1,n] [−n+1,n]的区间和满足条件
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while(t--){
long long n;
cin >> n;
cout << -n + 1 << ' ' << n << '\n';
}
return 0;
}
B
此题是NOIP原题
https://ac.nowcoder.com/acm/problem/16668
- 这个问题是让你找 n 0 , n 1 , n 2 . . . n^0,n^1,n^2... n0,n1,n2...的第 k k k小是谁,我们可以手推一下,第一小显然是 n 0 n^0 n0,第二小是 n 1 n^1 n1,第三小是 n 0 + n 1 n^0+n^1 n0+n1,第四小是 n 2 n^2 n2,以此类推,如果对二进制非常敏感的话能够发现每个数都是一个二进制数,且二进制数正好是按照次序来的,也就是第一小是 ( 1 ) 2 (1)_2 (1)2,第二小是 ( 10 ) 2 (10)_2 (10)2,第三小是 ( 11 ) 2 (11)_2 (11)2,那么第 k k k小就是 k k k所对应的二进制数,这样我们枚举 k k k所对应的二进制数的每一位,累加结果即可
- 这道题很长时间之前第一次做的时候我确实没看出这一层,但是现在看到这个题就很简单了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD = 1e9 + 7;
ll fastpow(ll base, ll power){
ll ans = 1;
while(power){
if(power & 1) ans = ans * base % MOD;
base = base * base % MOD;
power >>= 1;
}
return ans;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
long long n, k;
cin >> t;
while(t--){
cin >> n >> k;
ll ans = 0;
ll m = 0;
while(k){
if(k & 1){
ans = (ans + fastpow(n, m)) % MOD;
}
m += 1;
k >>= 1;
}
cout << ans << '\n';
}
return 0;
}
C
我们的目的是让这一个字符串里面的所有字符都变成目标字符,操作方式是这样的,选择一个数 x ∈ [ 1 , n ] x\in[1,n] x∈[1,n],对于字符串的每一个位置,如果位置下标不能被 x x x,整除,那么就用目标字符替代这个位置,现在问最小的操作数
- 显然如果我们选择 n n n,那么除了 n n n以外的其他位置都会变成目标字符,之后我们再选择 n − 1 n-1 n−1即可,所以最多操作次数就是 2 2 2;操作数为 0 0 0的情况是显然的;那么现在问题的关键就是操作数为 1 1 1时候该怎么办
- 如果有一个数不是任何非目标字符的下标的因子,那么操作次数就是1,换句话说,如果一个因子能够达到的所有的下标都是目标字符,那么操作次数就是1, x x x就是这个因子,我们可以使用一个 O ( n n ) O(n\sqrt n) O(nn)的类埃式筛法来解决这个问题
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t, m;
string s;
char c;
cin >> t;
while(t--){
cin >> m >> c;
cin >> s;
int nm = 0;
for(int i=0;i<m;i++){
if(s[i] != c) nm += 1;
}
if(nm == 0){
cout << 0 << '\n';
continue;
}
bool found = false;
for(int d=1;d<=m;d++){
int cc = d;
for(int i=d;i<=m;i+=d){
if(s[i - 1] != c){
cc = -1;
break;
}
}
if(cc != -1){
found = true;
cout << 1 << '\n';
cout << cc << '\n';
break;
}
}
if(!found){
cout << 2 << '\n';
cout << m - 1 << ' ' << m << '\n';
}
}
return 0;
}
D
imposter:骗子
crewmate:船员
给出了若干组关系,表示为 u u u说 v v v是骗子或者船员,现在问透过这些关系,最多有多少骗子,如果存在矛盾关系那么输出 − 1 -1 −1
- 这道题的 i d e a idea idea我们也许能够发现,如果 u u u说 v v v是骗子,那么 u u u和 v v v肯定属于不同的集合;如果 u u u说 v v v是船员,他们肯定属于同一个集合,这样我们就能够将这些人分成若干组,这是并查集的思想
- 我们在上面分的组只是若干个划分,并没有偏见,所以接下来先让 u u u节点是骗子或者船员,在 D f s Dfs Dfs的过程中观察是否出现矛盾,也就是根据 u u u推导出的 v v v所在的组和它现在所在的组不一致
- 如果没有矛盾,取较大的数累加到答案中,因为这两种情况是互补的
- 从程序实现的角度,我们可以利用异或的性质使得程序变得简单,也就是如果两个人是同一组那么集合权值为 0 0 0,否则为 1 1 1,
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t, n, m, u, v;
string s;
cin >> t;
while(t--){
cin >> n >> m;
vector<vector<pair<int, int> > > g(n);
for(int i=0;i<m;i++){
cin >> u >> v >> s;
u -= 1;
v -= 1;
if(s[0] == 'i'){
g[u].push_back(make_pair(v, 1));
g[v].push_back(make_pair(u, 1));
}else{
g[u].push_back(make_pair(v, 0));
g[v].push_back(make_pair(u, 0));
}
}
vector<int> used(n);
vector<int> now(n);
vector<int> cnt(2);
bool flag = true;
int ans = 0;
function<void(int)> Dfs = [&](int u){
used[u] = 1;
cnt[now[u]] += 1;
for(auto i : g[u]){
int v = i.first;
if(!used[v]){
now[v] = (now[u] ^ i.second);
Dfs(v);
}else{
if(now[v] != (now[u] ^ i.second)){
flag = false;
}
}
}
};
for(int i=0;i<n;i++){
if(!used[i]){
cnt[0] = cnt[1] = 0;
now[i] = 0;
Dfs(i);
ans += max(cnt[0], cnt[1]);
}
}
cout << (flag ? ans : -1) << '\n';
}
return 0;
}
E1
问这颗二叉树可能的涂色方案
- 根据简单排列组合的知识,显然根节点有六种方案,剩下的所有节点都有四种方案,如果二叉树层数为 k k k,那么答案为 6 × 4 2 k − 2 6\times4^{2^k-2} 6×42k−2
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD = 1e9 + 7;
ll fastpow(ll base, ll power){
ll ans = 1;
while(power){
if(power & 1) ans = ans * base % MOD;
base = base * base % MOD;
power >>= 1;
}
return ans;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
ll k;
cin >> k;
cout << 6ll * fastpow(4, pow(2, k) - 2) % MOD;
return 0;
}
E2
先pass
F
有 s s s只动物和 n n n个围栏,现在说,如果你能够把这些动物放在这些围栏里面,且没有空的围栏,并且至少有一个连续段里面至少有 k k k只动物,那么这个农场就是 l u c k y lucky lucky农场;同时,如果任何的没有空围栏的分配方式都能让这个农场成为 l u c k y lucky lucky农场,那么这个农场就是一个 i d e a l ideal ideal农场
- 现在给出农场的 s , n , k s,n,k s,n,k,问这个农场是不是 l u c k y lucky lucky农场
- 构造题,首先,如果 s < n s<n s<n显然我们随意构造都是非法;如果 s = n s=n s=n,那么随意构造都是合法;如果 s > n s>n s>n,考虑一种最坏情况的非法构造如下 1 , 1 , 1 , 1... ⏟ k − 1 个 1 , k + 1 , 1 , 1 , 1 , 1 , . . . ⏟ k − 1 个 1 , k + 1 , 1 , 1... \underbrace{1,1,1,1...}_{k-1个1},k+1,\underbrace{1,1,1,1,...}_{k-1个1},k+1,1,1... k−1个1 1,1,1,1...,k+1,k−1个1 1,1,1,1,...,k+1,1,1...
- 我们先给每一个围栏里面放上一个动物,这需要 n n n只动物,剩下 s − n s-n s−n只动物,然后 k k k的倍数的位置需要多放上 k k k个,需要动物的数量就是 ⌊ s k ⌋ × k \lfloor\frac{s}{k}\rfloor\times k ⌊ks⌋×k,所以构造出这种非法情况需要的条件是 s − n ≥ ⌊ s k ⌋ × k s-n\geq \lfloor\frac{s}{k}\rfloor\times k s−n≥⌊ks⌋×k,这个时候输出
NO
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while(t--){
long long s, n, k;
cin >> s >> n >> k;
if(s == k) cout << "YES\n";
else if(s < k) cout << "NO\n";
else if(s - n < n / k * k) cout << "YES\n";
else cout << "NO\n";
}
return 0;
}