求逆序对。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
string s;
cin >> s;
long long answer = 0;
int current = 0;
for (auto c : s) {
if (c == 'B') {
++current;
} else {
answer += current;
}
}
cout << answer << endl;
return 0;
}
从大到小贪心即可。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
vector<int> a(n);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
sort(a.begin(), a.end());
vector<int> b(n);
for (int i = n - 1; ~i; --i) {
b[i] = 1;
while (b[i] <= a[i]) {
b[i] <<= 1;
}
b[i] -= a[i];
}
int answer = 0;
map<int, int> c;
for (int i = 0; i < n; ++i) {
++c[a[i]];
}
for (int i = n - 1; ~i; --i) {
if (c[a[i]]) {
--c[a[i]];
if (c[b[i]]) {
--c[b[i]];
++answer;
}
}
}
printf("%d\n", answer);
return 0;
}
二分答案,如果 a i < a i + 1 a_i<a_{i+1} ai<ai+1 则 s i + 1 s_{i+1} si+1 是 s i s_i si 后面添若干个 0 0 0 ,否则去掉 a i + 1 a_{i+1} ai+1 之后的位然后加 1 1 1 ,拿个map模拟进位即可。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
vector<int> a(n);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
auto check = [&](int b) {
map<int, int> s;
for (int i = 0; i < n - 1; ++i) {
if (a[i] >= a[i + 1]) {
while (!s.empty() && s.rbegin()->first >= a[i + 1]) {
s.erase(--s.end());
}
for (int j = a[i + 1] - 1; ~j; --j) {
++s[j];
if (s[j] == b) {
s.erase(j);
} else {
break;
}
if (!j) {
return false;
}
}
}
}
return true;
};
bool flag = true;
for (int i = 0; i < n - 1; ++i) {
if (a[i] >= a[i + 1]) {
flag = false;
break;
}
}
if (flag) {
puts("1");
return 0;
}
int l = 2, r = n + 1;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
printf("%d\n", r);
return 0;
}
显然第一个人会一直向下走,假设当前第二个人能到 [ 1 , r ] [1,r] [1,r] ,如果下一行 [ 1 , r ] [1,r] [1,r] 有障碍,那么游戏就结束了;如果 r + 1 r+1 r+1 没有障碍,则可以往右走一步,变成 [ 1 , r + 1 ] [1,r+1] [1,r+1] 。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n, m, q;
scanf("%d %d %d", &n, &m, &q);
vector<int> mins(n, m);
for (int i = 0; i < q; ++i) {
int x, y;
scanf("%d %d", &x, &y);
--x;
--y;
mins[x] = min(mins[x], y);
}
int right = 0;
for (int i = 1; i < n; ++i) {
if (right >= mins[i]) {
printf("%d\n", i);
return 0;
}
if (right + 1 < mins[i]) {
++right;
}
}
printf("%d\n", n);
return 0;
}
注意到 x x x 的答案大概只跟 1 1 1 到 x x x 的链前缀最大值的位置相关, 并且 x x x 能到的一定是 p a r e n t x parent_x parentx 能到的超集,对答案差分之后枚举每个前缀最大值算算贡献就行了。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
vector<vector<int>> adj(n);
for (int i = 0; i < n - 1; ++i) {
int x, y;
scanf("%d %d", &x, &y);
--x;
--y;
adj[x].push_back(y);
adj[y].push_back(x);
}
vector<int> up(n), parent(n, -1);
function<void(int)> dfs = [&](int x) {
if (x) {
up[x] = max(up[parent[x]], parent[x]);
}
for (auto y : adj[x]) {
if (y != parent[x]) {
parent[y] = x;
dfs(y);
}
}
};
dfs(0);
vector<int> p(n), size(n);
for (int i = 0; i < n; ++i) {
p[i] = i;
if (i) {
size[i] = 1;
}
}
auto find = [&](int x) {
while (x != p[x]) {
x = p[x] = p[p[x]];
}
return x;
};
vector<vector<int>> sub(n);
for (int i = 1; i < n; ++i) {
sub[up[i]].push_back(i);
}
vector<int> value(n), answer(n);
for (int i = 0; i < n; ++i) {
for (auto x : sub[i]) {
if (x < i) {
answer[x] = size[find(x)];
} else {
answer[x] = 1;
for (auto y : adj[x]) {
if (y < i) {
if (y != parent[x]) {
value[y] = size[find(y)];
}
answer[x] += size[find(y)];
}
}
}
}
for (auto j : adj[i]) {
if (j < i) {
int x = find(i), y = find(j);
size[x] += size[y];
p[y] = x;
}
}
}
vector<int> pos(n);
vector<int> st;
function<void(int)> solve = [&](int x) {
pos[x] = st.size();
st.push_back(x);
if (x) {
answer[x] += answer[up[x]] - value[st[pos[up[x]] + 1]];
}
for (auto y : adj[x]) {
if (y != parent[x]) {
solve(y);
}
}
st.pop_back();
};
solve(0);
for (int i = 1; i < n; ++i) {
printf("%d%c", answer[i], i == n - 1 ? '\n' : ' ');
}
return 0;
}
假设 1 1 1 是根,每个集合选出的是 ( u i , p a r e n t u i ) (u_i, parent_{u_i}) (ui,parentui) ,那么 u i u_i ui 一定是一个 [ 2 , n ] [2,n] [2,n] 的排列,用网络流求出这样的一组 u i u_i ui ,然后贪心构造 p a r e n t u i parent_{u_i} parentui :初始将 1 1 1 加入队列,然后如果某个集合有 1 1 1 ,那直接将 p a r e n t u i parent_{u_i} parentui 设为 1 1 1 并把 u i u_i ui 加入队列。考虑这时候如果无解,说明初始的图并不连通,所以任意求一组 u i u_i ui 都不会影响答案。
#include
using namespace std;
const int inf = 0x3f3f3f3f;
namespace flow {
struct edge_t {
int to, cap, rev;
edge_t(int t, int c, int r) {
to = t;
cap = c;
rev = r;
}
};
int n, source, sink, answer;
vector<vector<edge_t>> adj;
vector<int> dist, current;
void init(int v, int s, int t) {
n = v;
source = s;
sink = t;
answer = 0;
adj.clear();
adj.resize(n);
dist.resize(n);
current.resize(n);
}
void add(int x, int y, int c) {
adj[x].push_back(edge_t(y, c, adj[y].size()));
adj[y].push_back(edge_t(x, 0, adj[x].size() - 1));
}
bool bfs() {
queue<int> q;
for (int i = 0; i < n; ++i) {
dist[i] = -1;
}
dist[source] = 0;
q.push(source);
while (!q.empty()) {
int x = q.front();
q.pop();
for (auto e : adj[x]) {
if (e.cap && !~dist[e.to]) {
dist[e.to] = dist[x] + 1;
if (e.to == sink) {
return true;
}
q.push(e.to);
}
}
}
return false;
}
int dfs(int x, int f) {
if (x == sink) {
return f;
}
for (int &i = current[x]; ~i; --i) {
edge_t &e = adj[x][i];
if (e.cap && dist[e.to] == dist[x] + 1) {
int w = dfs(e.to, min(e.cap, f));
if (w) {
e.cap -= w;
adj[e.to][e.rev].cap += w;
return w;
}
}
}
return 0;
}
int max_flow() {
while (bfs()) {
for (int i = 0; i < n; ++i) {
current[i] = adj[i].size() - 1;
}
while (true) {
int flow = dfs(source, inf);
if (!flow) {
break;
}
answer += flow;
}
}
return answer;
}
}
using flow::source;
using flow::sink;
using flow::init;
using flow::add;
using flow::adj;
using flow::max_flow;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
vector<vector<int>> a(n - 1);
vector<vector<int>> v(n);
init(n * 2, n * 2 - 2, n * 2 - 1);
for (int i = 0; i < n - 1; ++i) {
int s;
scanf("%d", &s);
a[i].resize(s);
add(source, i, 1);
for (int j = 0; j < s; ++j) {
scanf("%d", &a[i][j]);
--a[i][j];
v[a[i][j]].push_back(i);
if (a[i][j]) {
add(i, a[i][j] - 1 + n - 1, 1);
}
}
}
for (int i = 0; i < n - 1; ++i) {
add(i + n - 1, sink, 1);
}
if (max_flow() != n - 1) {
puts("-1");
return 0;
}
vector<int> match(n - 1);
for (int i = 0; i < n - 1; ++i) {
for (auto e : adj[i]) {
if (e.to < n * 2 - 2 && !e.cap) {
match[i] = e.to - (n - 1) + 1;
}
}
}
vector<bool> visit(n);
vector<int> parent(n - 1);
int cc = 0;
function<void(int)> insert = [&](int x) {
++cc;
visit[x] = true;
for (auto i : v[x]) {
if (!visit[match[i]]) {
parent[i] = x;
insert(match[i]);
}
}
};
insert(0);
if (cc != n) {
puts("-1");
return 0;
}
for (int i = 0; i < n - 1; ++i) {
printf("%d %d\n", match[i] + 1, parent[i] + 1);
}
return 0;
}