AtCoder Regular Contest 097

传送门

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\)出走一次的路径然后减去即可。

思路主要就是这样,把冗余的东西减去,问题转换为子树问题,然后状态转移有点繁琐,我就没写代码了(雾)。

posted @ 2019-10-23 14:17  heyuhhh  阅读(168)  评论(0编辑  收藏  举报