C - K-th Substring
题意:
给出一个字符串,求其第(k)小子串,(kleq 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] << '
';
}
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 << '
';
}
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] << '
';
}
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)出走一次的路径然后减去即可。
思路主要就是这样,把冗余的东西减去,问题转换为子树问题,然后状态转移有点繁琐,我就没写代码了(雾)。