A. Love Story
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef vector<int> vi;
typedef vector<ll> vll;
#define lowbit(S) ((S) & -(S))
#define pb push_back
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int TC;
cin >> TC;
string r = "codeforces";
while (TC--){
string s;
cin >> s;
int cnt = 0;
for (int i = 0; i < (int)s.size(); ++i){
if (s[i] != r[i]) ++cnt;
}
cout << cnt << endl;
}
return 0;
}
B. Blank Space
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef vector<int> vi;
typedef vector<ll> vll;
#define lowbit(S) ((S) & -(S))
#define pb push_back
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int TC;
cin >> TC;
while (TC--){
int n;
cin >> n;
vi a(n);
for (auto& x : a) cin >> x;
int ans = 0, cnt = 0;
for (auto x : a){
if (x == 0) cnt += 1;
else cnt = 0;
ans = max(ans, cnt);
}
cout << ans << endl;
}
return 0;
}
总结:不是简单的O(n)iteration,里面有一个很经典的greedy思路,就是选择当前最优,如果当前数字非0,那么再继续搜索下去肯定不如从0开始重新计数搜索...同类问题为找连续区间问题:找左右端点,找区间连续值,都可以理解为贪心策略
C. Mr. Perfectly Fine
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef vector<int> vi;
typedef vector<ll> vll;
#define lowbit(S) ((S) & -(S))
#define pb push_back
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int TC;
cin >> TC;
while (TC--){
int n, m;
string s;
cin >> n;
int a = 1e6, b = 1e6, c = 1e6;
for (int i = 0; i < n; ++i){
cin >> m >> s;
if (s[0] == '1'){b = min(m, b);}
if (s[1] == '1'){a = min(m, a);}
if (s[0] == '1' && s[1] == '1'){c = min(c, m);}
}
if (a != 1e6 && b != 1e6)
cout << min(a + b, c) << endl;
else cout << -1 << endl;
}
return 0;
}
总结:简单的统计问题,看到题目的时候脑子卡了两分钟,在想如何组织数据,要不要按时间或者什么东西排个序再去判定...思考过程中思路延申到使用变量来记录3种情况各需要消耗多少时间,最后比较一下时间即可得出结果.感觉也可以归纳为贪心问题。看了一下题目tag有一个:bitmask..暂时想不到如何用位运算写,因为读输入的时候两个01数字目前只能想到用字符串或者字符去读。
感觉题目有点像动态规划,又捋了一下,确定不是动态规划。因为dp有bottom-up和up-down两种策略,都是将问题分解成一个个sub-case,最后递推出最优解,对于这个问题,我觉得dp不行,虽然它也是一步一步的递推,但是它是制定了一个贪心的选择策略,总是选择当下case的最优解,最后得到全局解。
D. Gold Rush
题意:给定数字n和m,问n经过多次分割后能否得到m,分割只能是当前的n分成两份,一份是另一份大小的2倍。
思路:一眼看过去就是个二叉树形结构,左子树是自己的3分之1,右子树是自己的3分之2,直到该节点不能被3整除。就dfs遍历看看能不能到达有解的叶节点即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef vector<int> vi;
typedef vector<ll> vll;
#define lowbit(S) ((S) & -(S))
#define pb push_back
void backtrack(bool& ok, const int& target, int cur){
if (ok == true || target == cur){ok = true; return;}
if (cur < target || cur % 3 != 0) {return;}
backtrack(ok, target, cur / 3 * 2);
backtrack(ok, target, cur / 3);
}
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int TC;
cin >> TC;
while (TC--){
int n, m;
cin >> n >> m;
bool ok = false;
backtrack(ok, m, n);
cout << (ok ? "YES" : "NO") << endl;
}
return 0;
}
总结:感觉batrack和dfs的区别还不是理的很清楚,一般来说dfs是往深了走,遍历所有的节点找解析。 backtrack是根据当前的状态往下一个状态传导,complete search,有一个计算下一步的状态和一个恢复状态的过程,不过大体结构都差不多,感觉这个题目应该算dfs。
E. The Lakes
题意:给一个n*m的矩阵,找出深度最大的块,块的深度是该块每个点的深度的和,并且该块内所有的点是连通的(即不存在深度为0的点)
思路:dfs,固定参数是一个water数组和一个vis数组(记录该点有没有访问),可变参数是当前点的坐标和一个计数器。然后对每个点进行dfs并计数,找出计数最大的water区域即可
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef vector<int> vi;
typedef vector<ll> vll;
#define lowbit(S) ((S) & -(S))
#define pb push_back
vi dx{-1, 1, 0, 0};
vi dy{0, 0, -1, 1};
bool isVaild(int n, int m, int i, int j){
if (i >= 0 && i < n && j >= 0 && j < m) return true;
return false;
}
void backtrack(vector<vi>& water, vector<vector<bool>>& vis, int n, int m, int i, int j, int& cnt){
if (isVaild(n, m, i, j) == false || vis[i][j] == true || water[i][j] == 0) return;
vis[i][j] = true;
cnt += water[i][j];
for (int k = 0; k < 4; ++k){
int px = i + dx[k], py = j + dy[k];
backtrack(water, vis, n, m, px, py, cnt);
}
}
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int TC;
cin >> TC;
while (TC--){
int n, m;
cin >> n >> m;
vector<vi> a(n, vector<int>(m));
for (int i = 0; i < n; ++i){
for (int j = 0; j < m; ++j) cin >> a[i][j];
}
vector<vector<bool>> vis(n, vector<bool> (m, false));
int ans = 0;
for (int i = 0; i < n; ++i){
for (int j = 0; j < m; ++j) if (vis[i][j] == false){
int cnt = 0;
backtrack(a, vis, n, m, i, j, cnt);
ans = max(ans, cnt);
}
}
cout << ans << endl;
}
return 0;
}
总结:其实这个应该还是dfs,不应该写成backtrack,不过上个题是树形结构的dfs,这个题是图结构的dfs。
F. Forever Winter
题意:给定n,m代表节点数和边数,其中这些节点是一个雪花形的结构,即中间有一个中心节点c,跟c相连的有x个节点,然后这x各节点每个又连接了y个节点。给出n和m,求x和y。题目保证x和y greater than 1。 2 <= n <= 200
思路:图的结构是固定的,而且x,y大于1,所以对于每个叶子节点来说,它们只有一条边。采用邻接表的数据结构,然后统计每个点边的数量,可以将叶子节点筛选出来,剩下的节点就是中心节点C和与C相连的第二层节点。
有两种情况:
如果C和第一层节点的边的数量不相等,说明出现频次为1的点代表的边数,就是x的数量,剩下的频次-1就是第一层节点的边的数量(减一是因为在统计时第一层节点与点C也有一条边,要求出Y的数值需要减去这个边)。
如果C与第一层节点的边的数量的统计值相等,为t,说明x = t, y = t - 1。
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef pair<int, int> pii;
typedef vector<pii> vii;
#define lowbit(S) ((S) & -(S))
#define pb push_back
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int TC;
cin >> TC;
while (TC--){
int n, m;
cin >> n >> m;
vector<vector<int>> AL(n + 1);
for (int i = 0; i < m; ++i){
int a, b;
cin >> a >> b;
AL[a].pb(b);
AL[b].pb(a);
}
map<int, int> mapp;
for (int i = 1; i <= n; ++i){
mapp[(int)AL[i].size()] += 1;
}
pair<int, int> ans{0, 0};
if (mapp.size() == 3){
for (auto x : mapp){
if (x.first == 1) continue;
else if (x.second == 1) ans.first = x.first;
else ans.second = x.first - 1;
}
}
if (mapp.size() == 2){
for (auto x : mapp){
if (x.first > 1 && x.second > 1){ans.first = x.first; ans.second = x.first - 1;}
}
}
cout << ans.first << " " << ans.second << endl;
}
return 0;
}
总结:题目类型没怎么见过,一眼看过去有点生疏,不知道怎么解,就采用了这种simulation式的解法。然后感觉AL数组下次可以开大一点,题目虽然说了n的范围上限是200,但是并没有说每个点是从1开始往上计数的,也就是说有的题目可能会出现大数的情况,如果想数组开稍微小一点最好开一个discrete映射一下。 最后用了map,是因为感觉不同的频次最多只有3个,如果再开个数组去记录的话要浪费很多的空间。
G. Hits Different
思路:先预处理出来了一个这种三角形的数组,然后对于每个输入n,找到所处的行和列下标,然后dfs访问记录数值。
TLE了,晚点更新
TLE代码
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef pair<int, int> pii;
typedef vector<pii> vii;
#define lowbit(S) ((S) & -(S))
#define pb push_back
class UnionFind{
private:
vi fa, setSize, d;
int numSets;
public:
UnionFind(int m){
fa.assign(m, 0); for (int i = 0; i < m; ++i) fa[i] = i;
setSize.assign(m, 1);
d.assign(m, 0);
numSets = m;
}
int findSet(int x){
if (fa[x] == x) return x;
int root = findSet(root);
d[x] += d[fa[x]];
return fa[x] = root;
}
bool isSameset(int x, int y){return findSet(x) == findSet(y);}
int numDisjointSets(){return numSets;}
int numOFSets(int x){return setSize[findSet(x)];}
void unionSet(int x, int y){
x = findSet(x), y = findSet(y);
if (x == y) return;
d[x] = setSize[y];
setSize[y] += setSize[x];
fa[x] = y;
numSets -= 1;
}
};
//0-indexed or 1-indexed?
class FenwickTree{
private:
vll ft;
void build(const vll& f){
int m = f.size() - 1;
ft.assign(m + 1, 0);
for (int i = 1; i <= m; ++i){
ft[i] += f[i];
if (i + lowbit(i) <= m) ft[i + lowbit(i)] += ft[i];
}
}
public:
FenwickTree(int m){ft.assign(m + 1, 0);}
FenwickTree(const vll& f){build(f);}
FenwickTree(const int& maxn, const vi& s){
vll f(maxn + 1, 0);
for (int i = 0; i < (int)s.size(); ++i) f[s[i]] += 1;
build(f);
}
int rsq(int j){ ll sum = 0; while(j > 0){sum += ft[j]; j -= lowbit(j);} return sum;}
int rsq(int i, int j){return rsq(j) - rsq(i - 1);}
void update(int p, ll v){
while(p < (int)ft.size()){ft[p] += v; p += lowbit(p);}
}
};
class SegmentTree{
private:
vi A, st, lazy;
int n;
int left(const int& p){return p << 1;}
int right(const int& p){return (p << 1) + 1;}
int conquer(int a, int b){return a == -1 ? b : b == -1 ? a : min(a, b);}
void build(int p, int l, int r){
if (l == r) st[p] = A[r];
else{
int m = (l + r) >> 1;
build(left(p), l, m);
build(right(p), m + 1, r);
st[p] = conquer(st[left(p)], st[right(p)]);
}
}
void propagate(int p, int l, int r){
if (lazy[p] != -1){
st[p] = lazy[p];
if (l != r) lazy[left(p)] = lazy[right(p)] = lazy[p];
else A[l] = lazy[p];
lazy[p] = -1;
}
}
void update(int p, int l, int r, int i, int j, int val){
propagate(p, l, r);
if (i > j) return;
if (l >= i && r <= j) {lazy[p] = val; propagate(p, l, r);}
else{
int m = (l + r) >> 1;
update(left(p), l, m, i, min(m, j), val);
update(right(p), m + 1, r, max(m + 1, i), j, val);
int lsubtree = (lazy[left(p)] == -1) ? st[left(p)] : lazy[left(p)];
int rsubtree = (lazy[right(p)] == -1) ? st[right(p)] : lazy[right(p)];
st[p] = (lsubtree <= rsubtree) ? st[left(p)] : st[right(p)];
}
}
int rmq(int p, int l, int r, int i, int j){
propagate(p, l, r);
if (i > j) return -1;
if (l >= i && r <= j) return st[p];
else{
int m = (l + r) >> 1;
return conquer(rmq(left(p), l, m, i, min(m, j)), rmq(right(p), m + 1, r, max(m + 1, i), j));
}
}
public:
SegmentTree(const int& m): A(m), st(4 * m), lazy(4 * m, -1), n(m){}
SegmentTree(const vi& initialA):SegmentTree((int)initialA.size()){
}
void update(int l, int r, int val){update(1, 0, n - 1, l, r, val);}
int rmq(int i, int j){return rmq(1, 0, n - 1, i, j);}
};
vi sievePrimes(const int& x){
vi result;
bitset<40000> bs;
bs.set();
if (x > 4e4) {exit(111);}
for (int i = 2; i <= x; ++i){if (bs[i])
for (int j = i * i; j <= x; j += i)
bs[j] = 0;
result.push_back(i);
}
return result;
}
vi sievePrimeFactors(const int& x){
vi result(x + 1, 0);
for (int i = 2; i <= x; ++i) if (result[i] == 0){
for (int j = i; j <= x; j += i){
result[j] += 1;
}
}
return result;
}
template<typename T>
bool isPrime(const T& x){
if (x % 2 == 0) return false;
for (T i = 3; i * i <= x; i += 2) if (x % i == 0) return false;
return true;
}
template<typename T>
bool isPrime(const T& x, const vi& prime){
for (int i = 0; i < (int)prime.size() && prime[i] * prime[i] <= x; ++i) if (x % prime[i] == 0) return false;
return true;
}
template<typename T>
vi primeFactors(const T& x){
vi result;
while(x % 2 == 0){x /= 2; result.push_back(2);}
for (T i = 3; i * i <= x; i += 2){
while(x % i == 0){x /= i; result.push_back(i);}
}
if (x > 1) result.push_back(x);
return result;
}
template<typename T>
vi primeFactors(const T& x, const vi& prime){
vi result;
for (int i = 0; i < (int)prime.size() && prime[i] * prime[i] <= x; ++i){
while (x % prime[i] == 0){
x /= prime[i];
result.push_back(prime[i]);
}
}
if (x > 1) result.push_back(x);
return result;
}
template <typename T>
T gcd(T a, T b){return b == 0 ? a : gcd(b, a % b);}
template<typename T>
T lcm(T a, T b){return a / gcd(a, b) * b;}
template<typename T>
vi getFactors(const T& x){
vi result;
for (T i = 2; i * i <= x; ++i){
if (x % i == 0){
result.push_back(i);
if (i != x / i) result.push_back(x / i);
}
}
return result;
}
template<typename T>
int discrete(const T& x, map<T, int>& mapp, int& cnt){
if (!mapp.count(x)) mapp[x] = cnt++;
return mapp[x];
}
bitset<1000009> bs;
void backtrack(const vector<vi>& a, pii pos, ll& sum){
if (pos.first < 0 || pos.second < 0 || pos.second >= (int)a[pos.first].size() || bs[a[pos.first][pos.second]] == 0) return;
sum += ( 1ll * a[pos.first][pos.second] * a[pos.first][pos.second]);
bs[a[pos.first][pos.second]] = 0;
backtrack(a, {pos.first - 1, pos.second},sum);
backtrack(a, {pos.first - 1, pos.second - 1}, sum);
}
pii get_level(const vector<vi>& a, int n){
int l = 0, r = a.size() - 1;
while (l < r){
int mid = (l + r) >> 1;
int index = a[mid].size() - 1;
if (a[mid][index] >= n) r = mid;
else l = mid + 1;
}
for (int i = 0, j = (int)a[l].size() - 1; i <= j;){
int mid = (i + j) >> 1;
if (a[l][mid] > n) j = mid;
else if (a[l][mid] < n) i = mid + 1;
else if (a[l][mid] == n) return {l, mid};
}
return {0, 0};
}
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int TC;
cin >> TC;
vector<vi> a;
for (int i = 1, cnt = 1; cnt <= 1e6 + 5; ++i){
vi tmp(i);
for (int k = 0; k < i; ++k){
tmp[k] = cnt++;
}
a.pb(tmp);
}
while (TC--){
int n;
cin >> n;
bs.set();
ll ans = 0;
pii pos = get_level(a, n);
// cout << pos.first << " " << pos.second << endl;
backtrack(a, pos, ans);
cout << ans << endl;
}
return 0;
}
更新后的代码 求了一下前缀和dp计算
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long int ll;
typedef vector<int> vi;
typedef vector<vi> vvi;
typedef pair<int, int> pii;
typedef vector<ll> vll;
const int maxn = 1e6 + 10;
vector<vector<ll>> a;
vector<vector<ll>> prefix;
pii getLevel(const int& n){
int l = 0, r = (int)a.size() - 1;
pii result;
while (l < r){
int mid = (l + r) >> 1;
if (a[mid][(int)a[mid].size() - 1] >= n) r = mid;
else l = mid + 1;
}
result.first = l;
r = (int)a[l].size() - 1, l = 0;
while (l < r){
int mid = (l + r) >> 1;
if (a[result.first][mid] >= n) r = mid;
else l = mid + 1;
}
result.second = l;
return result;
}
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int TC;
cin >> TC;
for (int i = 1, k = 1; k <= maxn; ++i){
vll tmp(i);
for (int j = 0; j < i; ++j) tmp[j] = k++;
a.pb(tmp);
}
//for (auto x : a)for (auto y : x) cout << y << " "; cout << endl;
prefix = a;
for (int i = 0; i < (int)a.size(); ++i){
prefix[i][0] *= prefix[i][0];
for (int j = 1; j < (int)a[i].size(); ++j) prefix[i][j] = prefix[i][j]* prefix[i][j] + prefix[i][j - 1];
}
while (TC--){
int n;
cin >> n;
ll ans = 0;
pii pos = getLevel(n);
//cout << pos.first << ". " << pos.second << endl;
int l = pos.second, r = pos.second, level = pos.first;
while (level >= 0){
r = min(r, (int)prefix[level].size() - 1);
if (l > 0)ans += prefix[level][r] - prefix[level][l - 1];
else ans += prefix[level][r];
l -= 1;
level -= 1;
}
cout << ans << endl;
}
return 0;
}
H. Don't Blame Me
题意:求一个数组中子区间的and运算的1的数量为k个的子区间数量
思路:昨天写题的时候看成了连续区间的数量,一时没想到解法,可能会用到一些bitmask的库,今天学习一下。感觉应该是dp的问题,先试试backtrak+memoization,不行就上dp
5.10 21:02更
思路:经典的memoization搜索问题,直接暴力dfs + 状态存储即可
#include <bits/stdc++.h>
using namespace std;
#define lowbit(S) ((S) & -(S))
#define pb push_back
typedef long long int ll;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef pair<int, int> ii;
typedef vector<ii> vii;
typedef vector<bool> vb;
const int mod = 1e9 + 7;
int memoDfs(const vi& a, vector<vi>& memo, const int& k, int n, int mask){
if (n < 0){return 0;}
if (__builtin_popcount(mask) < k){return memo[n][mask] = 0;}
int& ans = memo[n][mask];
if (ans != -1) return ans;
int n1 = memoDfs(a, memo, k, n - 1, mask) % mod;
int n2 = memoDfs(a, memo, k, n - 1, (mask & a[n])) % mod;
return ans = ((__builtin_popcount(mask & a[n]) == k) + n1 + n2) % mod;
}
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int TC;
cin >> TC;
while (TC--){
int n, k;
cin >> n >> k;
vi a(n);
for (auto& x :a) cin >> x;
vector<vi> memo(n, vi(1 << 6, -1));
int ans = memoDfs(a, memo, k, n - 1, (1 << 6) - 1);
cout << ans << "\n";
}
return 0;
}
总结: 被题目搞懵了,子区间数量太多,不知道bottom-up的dp解法怎么写,就懵了,用暴力dfs会写,但是状态存储和表示用的不熟练..memoDfs就是bruteforce + memoization,无脑存状态就完事了