C - K-th Substring
题意:
给出一个字符串,求其第\(k\)小子串,\(k\leq 5\)。
思路:
因为\(k\)很小,所以答案长度不可能超过\(k\)。所以直接将所有的长度不超过\(k\)的串拿出来排序就行。Code
#include <bits/stdc++.h> #define MP make_pair #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() // #define Local using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 5005; string s; int k; vector<string> v; void run() { v.clear(); cin >> s >> k; int len = s.length(); for(int i = 1; i <= k; i++) { for(int j = 0; j + i - 1 < len; j++) { v.push_back(s.substr(j, i)); } } sort(all(v)); unique(all(v)); cout << v[k - 1] << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); #ifdef Local freopen("../input.in", "r", stdin); freopen("../output.out", "w", stdout); #endif run(); return 0; }
D - Equals
题意:
给出一个排列\(p\),并且给出多个二元组\((x_i,y_i)\),现在可以执行任意多次操作,每次可以交换\(p_{x_i},p_{y_i}\)。
问最终得到的排列中,\(p_i=i\)的最大个数是多少。
思路:
显然在交换的序列中,处在同一个连通块中的点能到达任意一个位置。
然后随便搞搞就行。Code
#include <bits/stdc++.h> #define MP make_pair #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() // #define Local using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 1e5 + 5; int n, m; int a[N], f[N]; int pos[N]; int find(int x) { return f[x] == x ? f[x] : f[x] = find(f[x]); } void Union(int x, int y) { int fx = find(x), fy = find(y); if(fx != fy) f[fx] = fy; } std::vector<int> v[N]; void run() { for(int i = 1; i <= n; i++) cin >> a[i]; for(int i = 1; i <= n; i++) f[i] = i, v[i].clear(); for(int i = 1; i <= m; i++) { int x, y; cin >> x >> y; Union(x, y); } for(int i = 1; i <= n; i++) { pos[i] = find(i); v[find(i)].push_back(a[i]); } int ans = 0; for(int i = 1; i <= n; i++) { for(auto it : v[i]) { if(pos[it] == i) ++ans; } } cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); #ifdef Local freopen("../input.in", "r", stdin); freopen("../output.out", "w", stdout); #endif while(cin >> n >> m) run(); return 0; }
E - Sorted and Sorted
题意:
现在将\(n\)个白球和\(n\)个黑球混在一起放置,每种球的标号都是一个排列,每个球都相应有一个\(1\)到\(n\)的标号。
现在执行一次操作可以交换相邻两个球的位置,问至少需要多少次操作,最终对于每类球而言,标号都是从小到大(即从\(1\)到\(n\))。
思路:
- 考虑只有一类球时,显然答案为逆序对数。
- 现在有两类球,这里我们将逆序对给“广义化”。假设我们前面已经放好了\(1\)到\(i-1\)的白球以及\(1\)到\(j\)的黑球,我们现在在当前这个位置放置一个白球,此时产生的贡献为\(x\),\(x\)表示之前有多少个球在\(i\)号白球后面,但现在在前面的个数。
- 这里我理解的就是将逆序对更加广义化,不仅仅是大小关系,这是一种先后关系。
- 那么我们可以将每个球的贡献利用树状数组预处理出来,之后直接\(dp\)枚举所有情况来放置就行。
感觉挺巧妙的,思路僵化还真不好想,但如果想到逐位来放枚举所有情况应该就比较好做了。Code
#include <bits/stdc++.h> #define MP make_pair #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() #define INF 0x3f3f3f3f // #define Local using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 2005; int a[N << 1], c[2][N]; char s[2]; int n; int cost_w[N][N], cost_b[N][N]; int dp[N][N]; int lowbit(int x) {return x & (-x);} void add(int id, int x) { for(; x < N; x += lowbit(x)) ++c[id][x]; } int query(int id, int x) { int res = 0; for(; x; x -= lowbit(x)) res += c[id][x]; return res; } void run() { memset(c, 0, sizeof(c)); for(int i = 1; i <= 2 * n; i++) { cin >> s >> a[i]; if(s[0] == 'W') { for(int j = 0; j <= n; j++) { cost_w[a[i] - 1][j] = i - 1 - query(0, a[i] - 1) - query(1, j); } add(0, a[i]); } else { for(int j = 0; j <= n; j++) { cost_b[j][a[i] - 1] = i - 1 - query(1, a[i] - 1) - query(0, j); } add(1, a[i]); } } memset(dp, INF, sizeof(dp)); dp[0][0] = 0; for(int i = 0; i <= n; i++) { for(int j = 0; j <= n; j++) { if(i) dp[i][j] = min(dp[i][j], dp[i - 1][j] + cost_w[i - 1][j]); if(j) dp[i][j] = min(dp[i][j], dp[i][j - 1] + cost_b[i][j - 1]); } } cout << dp[n][n] << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); #ifdef Local freopen("../input.in", "r", stdin); freopen("../output.out", "w", stdout); #endif while(cin >> n) run(); return 0; }
F - Monochrome Cat
题意:
给出一颗树,每个数的结点为黑点或者白点。
现在当处于一个结点上时有两种选择:
- 改变当前点的颜色;
- 走向相邻的一个点,并改变它的颜色。
最后要求从任意一个起点出发,将整棵树变为黑色的最少操作次数为多少。
思路:
- 显然,一个结点所需操作次数跟经过其次数的奇偶性有关。
- 直接统计不方便统计,我们可以首先将所有“末端”的黑点去掉,那么这颗树的所有叶子结点都为白色,问题就可以转换为子树问题。
- 显然每条边都会被走两次,但由于起始点的选择不同,可能会存在一条路径只会走一次。
- 那么先假设每条边都会走两次,最后树\(dp\)出走一次的路径然后减去即可。
思路主要就是这样,把冗余的东西减去,问题转换为子树问题,然后状态转移有点繁琐,我就没写代码了(雾)。