P6739 [BalticOI 2014 Day1] Three Friends
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n;
cin >> n;
string s, l, r;
cin >> s;
if(n % 2 == 0) {
cout << "NOT POSSIBLE\n";
return 0;
}
int m = n / 2;
//在s中删除一个字符,使得左右两边相等,并且能够得到的答案子串是唯一的。
//用左半段l和s-l(右边所有)进行逐位匹配,就相当于在s-l中删除了一个字符能否与l相同。
//用右半段时,也是同理。
l = s.substr(0, m);
r = s.substr(n - m, m);
bool f1 = false, f2 = false;
int j = 0;
for (int i = m; i < n && j < m; i++) {
if(s[i] == l[j]) j++;
}
if(j == m) f1 = true;
j = 0;
for (int i = 0; i < n - m && j < m; i++) {
if(s[i] == r[j]) j++;
}
if(j == m) f2 = true;
if(!f1 && !f2) {
cout << "NOT POSSIBLE\n";
}
else if(f1 && f2 && l != r) {
cout << "NOT UNIQUE\n";
}
else if(f1) cout << l << endl;
else cout << r << endl;
return 0;
}
cf821 D2. Zero-One (Hard Version)
题目大意:给定长度为n(n <= 5000)的两个01字符串a,b,现在你可以进行
的操作为:选择两个索引l < r,若l + 1 == r花费x, 否则花费y。
问使得a,b相等需要的最小花费。
记录a,b不相等处的索引,个数为奇输出-1。
x >= y简单,贪心:尽可能的选择不相邻的l和r。
x < y,贪心:尽可能选择相邻的或最近的。
记忆化搜索:
1.选择l和l+1,2.选择r和r-1,3.选择l和r,想当于想让后续索引的选择更加接近。
using ll = long long;
const ll inf = 1e18;
ll f[5005][5005], n, x, y;
int id[5005], p;
ll dfs(int l, int r) {
if(l > r) return 0;
if(f[l][r] != -1) return f[l][r];
ll ans1 = dfs(l + 2, r) + min(y, x * (id[l + 1] - id[l]));
ll ans2 = dfs(l + 1, r - 1) + min(y, x * (id[r] - id[l]));
ll ans3 = dfs(l, r - 2) + min(y, x * (id[r] - id[r - 1]));
return f[l][r] = min({ans1, ans2, ans3});
}
void solve() {
cin >> n >> x >> y;
string a, b;
cin >> a >> b;
for (int i = 0; i < n; i++) {
if(a[i] != b[i]) {
id[p++] = i;
}
}
if(p & 1) {
p = 0;
cout << -1 << endl;
return;
}
ll ans = 0;
if(x >= y) {
int xt = 0;
for (int i = 0; i < p; i += 2) {
if(id[i] + 1 == id[i + 1]) {
xt++;
}
}
int ct = p / 2 - xt;
if(xt == 1 && !ct) {
ans = min(x, 2 * y);
}
else {
ans = xt * y + ct * y;
}
cout << ans << "\n";
p = 0;
return;
}
for (int i = 0; i < p; i++) {
for (int j = 0; j < p; j++) {
f[i][j] = -1;
}
}
cout << dfs(0, p - 1) << endl;
p = 0;
}
cf 899 div2 D
题目大意:给你一棵树(n个顶点,n - 1条边),每一个顶点的值为a[i],你需要求得的是每一个顶点为
根得时候,要使得所有顶点的值相等,需要付出的最小代价。
子树:假设选择顶点r作为树根。如果从i到r的简单路径包含v,那么顶点i属于v的子树。
你可以进行的操作:选定一个顶点v和一个非负整数c,把v的子树中的所有顶点的值异或上c,花费的
代价为sz[v]*c,sz[v]是子树的大小。
贪心:以哪个顶点为根,就把其余的所有顶点的值变成和根的值相同。
我们可以先求出以1为根结点所花费的代价,然后进行换根。
换根:又父节点u换到子节点v:ans[v] = ans[u] + 1LL * (n - sz[v] * 2) * (a[u] ^ a[v])。
相当于此时u的子树需要再算一次(u的子树的大小即为n-sz[v]),而v的子树不需要算了(v之前算了,所以要减去)。
void solve() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
vector<vector<int> > g(n);
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
u--, v--;
g[u].push_back(v);
g[v].push_back(u);
}
vector<ll> ans(n), sz(n);
auto dfs1 = [&](auto self, int u, int fa) -> void {
sz[u] = 1;
for (int v : g[u]) {
if(v == fa) continue;
self(self, v, u);
sz[u] += sz[v];
ans[0] += sz[v] * (a[u] ^ a[v]);
}
};
dfs1(dfs1, 0, -1);
auto dfs2 = [&](auto self, int u, int fa) -> void {
for (int v : g[u]) {
if(v == fa) continue;
ans[v] = ans[u] + 1LL * (n - sz[v] * 2) * (a[u] ^ a[v]);
self(self, v, u);
}
};
dfs2(dfs2, 0, -1);
for (int i = 0; i < n; i++) {
cout << ans[i] << " \n"[i == n - 1];
}
}