标签:time 应该 否则 要求 git pool distinct 生成树 queue
给你一个字符串\(S\),可能包含小写字母或大写字母或数字。让你改变其中的一些字符,使得改变后的字符串包含小写字母、大写字母和数字。输出改变后的字符串。保证有解。
扫描一遍,找出小写字母、大写字母和数字出现的第一个位置。然后再重新扫一遍,如果说\(i\)不是小写字母、大写字母或数字出现的第一个位置,就可以把\(S_i\)改变成没有出现过的一种字符。
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
#define is_lower(a) (a >= 'a' && a <= 'z')
#define is_upper(a) (a >= 'A' && a <= 'Z')
#define is_digit(a) (a >= '0' && a <= '9')
void solve() {
string s;
cin >> s;
int n = s.length();
int pos_lower = -1, pos_upper = -1, pos_dig = -1;
for (int i = 0; i < n; ++i) {
if (is_lower(s[i])) {
pos_lower = i;
break;
}
}
for (int i = 0; i < n; ++i) {
if (is_upper(s[i])) {
pos_upper = i;
break;
}
}
for (int i = 0; i < n; ++i) {
if (is_digit(s[i])) {
pos_dig = i;
break;
}
}
for (int i = 0; i < n; ++i) {
if (i == pos_lower) continue;
if (i == pos_upper) continue;
if (i == pos_dig) continue;
if (pos_lower == -1) s[i] = 'a', pos_lower = 0;
else if (pos_upper == -1) s[i] = 'A', pos_upper = 0;
else if (pos_dig == -1) s[i] = '0', pos_dig = 0;
}
cout << s << endl;
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int tests; cin >> tests;
while (tests--) solve();
return 0;
}
给你一个范围\([l,r],r-l\mod 2=1,l\le r\),要你把范围里面的所有的整数分成\(\dfrac{r-l+1}{2}\)个部分,每一个部分有两个数,且这两个数互质。如果能办到,就输出YES
,然后输出方案;否则输出NO
。\(1\le l<r\le 10^{18}\)
很容易发现每两个相邻的数是互质的,所以就可以把两个相邻的数分到一组,即\((l,l+1),(l+2,l+3),\ldots,(r-1,r)\)。
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
signed main() {
ll l, r;
cin >> l >> r;
puts("YES");
for (ll i = l; i <= r; i += 2)
printf("%lld %lld\n", i, i + 1);
return 0;
}
给你一个多重集合\(S\),如果\(x\in S\)且\(x\)在\(S\)中只出现\(1\)次,则称\(x\)为好数。现在要你把\(S\)分成两个多重集合\(A,B\),使得\(A,B\)里面的好数的个数相等。如果办不到,输出NO
。\(1\le|S|\le 100\)
统计\(S\)中的好数的个数\(c\),如果\(c\mod2=0\),则直接把\(\dfrac{c}{2}\)个好数分给\(A\),其他的所有数都分给\(B\)。如果\(c\mod 2=1\),先把\(\dfrac{c-1}{2}\)个好数分给\(A\),其余的好数分给\(B\)。在剩下来的那些不是好数的数中,若存在\(d\)在\(S\)中出现的次数\(>2\),则可以把一个\(d\)分给\(A\),其他的\(d\)分给\(B\),这样\(A,B\)集合的好数就相等了。如果找不到这样的\(d\),那么答案就是NO
。
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
int n;
int a[105];
int mp[105];
bool vis[105];
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i], mp[a[i]]++;
int cnt = 0, cnt1 = 0;
for (int i = 1; i <= 100; ++i)
cnt += (mp[i] >= 3), cnt1 += (mp[i] == 1);
if (cnt == 0 && cnt1 % 2) return puts("NO") & 0;
puts("YES");
int cnt2 = 0, flag = 0;
for (int i = 1; i <= n; ++i)
if (mp[a[i]] == 1) {
++cnt2;
if (cnt2 * 2 <= cnt1) vis[i] = 0;
else vis[i] = 1;
}
else if (mp[a[i]] > 2) {
if (cnt1 % 2 && !flag)
vis[i] = 0;
else vis[i] = 1;
flag = 1;
}
else vis[i] = 'B';
for (int i = 1; i <= n; ++i)
putchar("AB"[vis[i]]);
return 0;
}
给定一个\(2\times n\)的棋盘,可以对上面的格子黑白染色,求染色后棋盘上的颜色相同联通块的个数正好为\(k\)的染色方案数。\(1\le n\le 2000\)
设\(dp_{i,j,c_1,c_2}\)表示当前染色到第\(i\)列,前面一共有\(j\)个联通块,第\(i-1\)列的颜色分别为\(c_1,c_2\)。
转移的话直接枚举\(c_1,c_2\),是\(O(1)\)的。总时间复杂度就是\(O(nk)\)。
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
constexpr int Maxn = 2005;
constexpr int mod = 998244353;
inline int add(int x, int y) { return (x += y) >= mod ? x - mod : x; }
inline void inc(int &x, int y) { (x += y) >= mod && (x -= mod); }
inline int mul(int x, int y) { return 1LL * x * y - 1LL * x * y / mod * mod; }
int n, k;
int dp[Maxn][Maxn][2][2];
int more(int a1, int a2, int b1, int b2) {
if (a1 == b1 && a2 == b2) return 0;
if (a1 == a2) {
if (b1 == a1 && b2 == a1) return 0;
return 1;
}
if (b1 == b2) return 0;
if (b1 == a1) return 0;
return 2;
}
signed main() {
cin >> n >> k;
dp[1][1][0][0] = 1, dp[1][1][1][1] = 1;
dp[1][2][0][1] = 1, dp[1][2][1][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= k; ++j) {
for (int a1 = 0; a1 < 2; ++a1) {
for (int a2 = 0; a2 < 2; ++a2) {
for (int b1 = 0; b1 < 2; ++b1) {
for (int b2 = 0; b2 < 2; ++b2) {
inc(dp[i + 1][j + more(a1, a2, b1, b2)][b1][b2], dp[i][j][a1][a2]);
}
}
}
}
}
}
cout << add(add(dp[n][k][0][0], dp[n][k][0][1]), add(dp[n][k][1][0], dp[n][k][1][1])) << endl;
return 0;
}
给你一个由数字构成的字符串\(S\),问你有多少种划分方式,使得划分的每段不含前导0,并且每段的数字大小在\([l,r]\)之间。\(0\le l\le r\le 10^{10^{6}},0\le S\le10^{10^{6}}\)
先考虑一个DP:\(dp_i\)表示\(S_{i..|S|}\)这一段数字字符串有\(dp_i\)种划分方式满足要求。
很显然,若\(S_i=0\),则当\(l=0\)时,\(dp_i=dp_{i+1}\),否则\(dp_i=0\)。
若\(S_i\neq 0\),则要找到最小的\(L\),最大的\(R\),使得\(S_{i..(L-1)}\ge l,S_{i..(R-1)}\le r\),那么这样\(dp_i=dp_L+dp_{L+1}+dp_{L+2}+\dots+dp_{R}\)。
但这样时间复杂度为\(O(|S|^2)\),显然是不行的。
首先那个求和部分可以用后缀和进行优化,现在要做的就是要快速找到\(L,R\)。
进一步观察可以发现,若\(S_{i,i+|l|-1}\ge l\),则\(L=i+|l|\),否则\(L=i+|l|+1\),对于\(R\)也是同理。所以现在问题就转化成了判断\(S_{i+|l|-1},l\)和\(S_{i+|r|-1},r\)的大小关系。
常规的判断方法是枚举\(j=0\rightarrow|l|-1\),若\(S_{i+j}\neq l_j\),则\(S_{i+|l|-1}\)和\(l\)的大小就是\(S_{i+j}\)和\(l_j\)的大小。设\(lcp(S_{i+|l|-1},l)=lcp\),则\(S_{i+|l|-1}\)和\(l\)的大小就是\(S_{i+lcp}\)和\(l_{lcp}\)的大小。现在问题就转化成了快速求\(lcp(S_{i+|l|-1},l)\)。这可以通过哈希或对\(l+S\)建立z function
求得。
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
constexpr int mod = 998244353;
inline int add(int x, int y) { return (x += y) >= mod ? x - mod : x; }
inline void inc(int &x, int y) { (x += y) >= mod && (x -= mod); }
constexpr int Maxn = 2000005;
int n, ln, rn;
char a[Maxn], l[Maxn], r[Maxn];
int zl[Maxn], zr[Maxn];
inline void buildZ(int n, const char *s, int *z) {
for (register int i = 1, l = 0, r = 0; i < n; ++i) {
if (i <= r) z[i] = (z[i - l] < r - i + 1 ? z[i - l] : r - i + 1);
while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i];
if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
}
}
inline int cmp(int len, int *z, const char *s, int pos) {
if (n - pos + 1 < len) return -1;
int x = z[pos + len + 1];
if (x == len) return 0;
return a[pos + x] < s[1 + x] ? -1 : 1;
}
int dp[Maxn], suf[Maxn];
signed main() {
scanf("%s%s%s", a + 1, l + 1, r + 1);
n = strlen(a + 1);
ln = strlen(l + 1);
rn = strlen(r + 1);
l[ln + 1] = r[rn + 1] = '$';
strcat(l + 1, a + 1);
strcat(r + 1, a + 1);
buildZ(ln + 1 + n, l + 1, zl + 1);
buildZ(rn + 1 + n, r + 1, zr + 1);
//for (int i = 1; i <= n + ln + 1; ++i) fprintf(stderr, "zl[%d] = %d\n", i, zl[i]);
//for (int i = 1; i <= n + rn + 1; ++i) fprintf(stderr, "zr[%d] = %d\n", i, zr[i]);
dp[n + 1] = suf[n + 1] = 1;
for (int i = n; i >= 1; --i) {
if (a[i] == '0') {
if (l[1] == '0') dp[i] = dp[i + 1];
else dp[i] = 0;
suf[i] = add(suf[i + 1], dp[i]);
continue;
}
int cmpl = cmp(ln, zl, l, i);
int cmpr = cmp(rn, zr, r, i);
int L = min(ln + i + (cmpl == -1), n + 2);
int R = min(rn + i - (cmpr == 1), n + 2);
//fprintf(stderr, "i = %d, cmpl = %d, cmpr = %d, L = %d, R = %d\n", i, cmpl, cmpr, L, R);
dp[i] = (L <= R) ? add(suf[L], mod - suf[R + 1]) : 0;
//fprintf(stderr, "dp[%d] = %d\n", i, dp[i]);
suf[i] = add(suf[i + 1], dp[i]);
}
printf("%d\n", dp[1]);
return 0;
}
给你一个图,有\(n\)个点,\(m\)条边,保证\(m-n\le 20\)。有\(q\)次询问,每次询问求两个点\(x,y\)之间的最短路。
我们把这个图的其中一个生成树给拿出来,那么还有最多\(21\)条边不在这个生成树上。
这个最短路可能有两种:经过不在生成树上的边,和不经过的。
不经过生成树上的边的路径就只有\(1\)条,直接在生成树上找lca,然后\(d=根到x的距离+根到y的距离-2\times 根到lca的距离\)
然后考虑经过不在生成树上的边。
如果一个路径经过一个边,那么它必定经过这条边的两个端点。所以我们对所有这些不在生成树上的边的端点跑一遍dijkstra,这些点最多只有\(42\)个。设\(dist_{i,j}\)表示编号为\(i\)的这种点到原图上\(j\)号点的距离。那么经过不在生成树上的边的最短路\(ans=\min\limits_{i=0}^{41}\{dist_{i,x}+dist_{i,y}\}\)
最后把这两种最短路取\(\min\)即可。
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
#define fi first
#define se second
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
constexpr int Maxn = 100005;
#define int ll
vector<pair<int, int>> g[Maxn];
int n, m, q;
int par[Maxn][20], dep[Maxn];
ll dist[Maxn][50], deep[Maxn];
vector<int> vec;
bool vis[Maxn], low[Maxn];
void dfs(int u, int fa) {
vis[u] = true;
for (auto pr: g[u]) {
int v = pr.fi;
if (v != fa) {
if (!vis[v]) {
par[v][0] = u;
dep[v] = dep[u] + 1;
deep[v] = deep[u] + pr.se;
dfs(v, u);
}
else low[u] = low[v] = true;
}
}
}
inline int get_lca(int u, int v) {
if (dep[u] > dep[v]) swap(u, v);
for (int i = 0; i < 20; ++i)
if ((dep[v] - dep[u]) >> i & 1)
v = par[v][i];
if (u == v) return v;
for (int i = 19; i >= 0; --i)
if (par[u][i] != par[v][i])
u = par[u][i], v = par[v][i];
return par[u][0];
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
int u, v; ll w;
cin >> u >> v >> w;
g[u].push_back({v, w});
g[v].push_back({u, w});
}
par[1][0] = 0;
dfs(1, 1);
for (int j = 1; j < 20; ++j)
for (int i = 1; i <= n; ++i)
par[i][j] = par[par[i][j - 1]][j - 1];
for (int i = 1; i <= n; ++i)
if (low[i]) vec.push_back(i);
for (int num = 0; num < (int)vec.size(); ++num) {
for (int i = 1; i <= n; ++i) dist[i][num] = lnf;
priority_queue<pair<ll, int>> pq;
dist[vec[num]][num] = 0;
pq.push({0, vec[num]});
while (!pq.empty()) {
ll d = -pq.top().fi;
int u = pq.top().se;
pq.pop();
if (d != dist[u][num]) continue;
for (auto pr: g[u]) {
int v = pr.fi;
ll w = pr.se;
if (dist[v][num] > dist[u][num] + w) {
dist[v][num] = dist[u][num] + w;
pq.push({-dist[v][num], v});
}
}
}
}
cin >> q;
while (q--) {
int u, v;
cin >> u >> v;
int lca = get_lca(u, v);
ll ans = deep[u] + deep[v] - deep[lca] * 2;
for (int i = 0; i < (int)vec.size(); ++i)
ans = min(ans, dist[u][i] + dist[v][i]);
cout << ans << endl;
}
return 0;
}
若多重集合\(S=\{(a_i,b_i)|1\le i\le k\}\),定义一个操作为:
你可以进行任何次数的操作。
定义\(f(S)\)为最小的代价,使得你可以花\(f(S)\)的代价使\(S\)里面的所有\(a_i\)不同。\(f(S)\)可以为负数。
你现在有\(n\)次询问,每次询问是在当前的\(S\)里面插入\((a_i,b_i)\)后询问\(f(S)\)。开始时\(S=\varnothing\)。保证\(1\le a_i\le 2\times 10^5,1\le b_i\le n\),且所有的\(b_i\)不同。
很容易地可以观察到当且仅当\(|a_i-a_j|\le1\),则可以交换\(a_i,a_j\)
首先先考虑若\(S\)里的\(a_i\)是一段连续值域,且互不相同怎么求。我们可以想到,\(b_i\)越大,就要尽可能的减少\(a_i\)。又因为\(S\)里的\(a_i\)是一段连续的值域且互不相同,所以\(S\)里的\(a_i\)可以随便排,所以最后的\(S\)应该是:对于任意\(a_i<a_j\),\(b_i>b_j\)。此时的代价应为\((a'_1-a_1)\times b_1+(a'_2-a_2)\times b_2+\dots+(a'_{|S|}-a_{|S|})\times b_{|S|}\),即\((a'_1\times b_1+a'_2\times b_2+\dots+a'_{|S|}\times b_{|S|})-(a_1\times b_1+a_2\times b_2+\dots+a_{|S|}\times b_{|S|})\)。
又因为当且仅当\(|a_i-a_j|\le1\),则可以交换\(a_i,a_j\),所以\(S\)的每一个极长\(a_i\)连续值域段是互相独立的,所以对于任意一个\(S\),\(f(S)=(a'_1\times b_1+a'_2\times b_2+\dots+a'_{|S|}\times b_{|S|})-(a_1\times b_1+a_2\times b_2+\dots+a_{|S|}\times b_{|S|})\)。
显然\(a_1\times b_1+a_2\times b_2+\dots+a_{|S|}\times b_{|S|}\)是很好求的,所以问题就转化为了求\(a'_1\times b_1+a'_2\times b_2+\dots+a'_{|S|}\times b_{|S|}\)。记这个式子为after。
我们先考虑在一个极长\(a_i\)连续值域段里面插入\((a_i,b_i)\)怎么做。我们维护这个极长\(a_i\)连续值域的最左端\(beg\),这样将\(b\)从小到大排序后可得\(after=b_1\times (beg+k-1)+b_2\times (beg+k-2)+\dots+b_k\times beg\),即\(after=beg\times(b_1+b_2+\dots+b_k)+(b_1\times(k-1)+b_2\times(k-2)+\dots+b_k\times0)\)。现在问题就是动态地在一个排好序的数组\(b\)中插入\(x\),插入完后求上面那个式子。\(beg\times(b_1+b_2+\dots+b_k)\)特别好求,每一次插入\(x\),答案就\(+x\times beg\)。对于\(b_1\times(k-1)+b_2\times(k-2)+\dots+b_k\times0\)我们可以用treap维护数组\(b\),每一次插入\(x\)时把\(b\)分成\(b_{1..pos}\)和\(b_{pos+1..k}\)。那么这个时候\(b_{1..pos}\)里的每一个\(b\)对答案的贡献\(+1\),\(x\)对答案的贡献\(+(k-pos)\times x\)。这个可以在treap上维护size和sum来快速实现。于是动态插入\((a_i,b_i)\)的单次时间复杂度是\(O(\log k)\)的。
接着我们考虑如何合并两个极长\(a_i\)连续值域段\(S,T\)。不妨设\(|S|\le|T|\)。一个很直接的想法就是直接将\(S\)里面的\(b_i\)依次插入到\(T\)里面。这样的时间复杂度是\(O(|S|\log|T|)\)。
然后我们考虑在一个普通的集合\(S\)里面插入\((a_i,b_i)\)。
我们用并查集维护那些极长\(a_i\)连续值域段已经合并。
如果\((a_i,b_i)\)属于之前的一个极长\(a_i\)连续值域段,那么直接在那个极长\(a_i\)连续值域段里插入\((a_i,b_i)\),同时还要占据这个连续值域段的右端点的右边一位。否则就新开创一个极长\(a_i\)连续值域段,长度为\(1\)。
在这之后还要看\((a_i,b_i)\)所属的极长\(a_i\)连续值域段是否能和它旁边的两个极长\(a_i\)连续值域段合并。两个极长\(a_i\)连续值域段\(S,T\)能合并的条件是\(r(S)+1=l(T)\)或\(r(T)+1=l(S)\),其中\(r(S)\)代表\(S\)的右端点,\(l(S)\)代表\(S\)的左端点。
每一次合并的时间复杂度是\(O(\min(|S|,|T|)\log N)\)的。由于是把小集合的元素的往大集合里面插入进去,每一个元素最多只会被插入\(\log N\)次,所以总时间复杂度最坏情况下是\(O((N\times \log N)\times \log N)=O(N\log^2N)\)。
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
inline int randint() {
return uniform_int_distribution<int>()(__gen);
}
constexpr int Maxn = 400005;
int n;
ll before, after;
struct node {
int size, fix;
node *l, *r;
int x;
ll sum;
node() { }
node(int x, node *l = NULL, node *r = NULL)
: x(x), l(l), r(r), size(1), fix(randint()) {
sum = x;
}
inline int lsize() const { return l ? l->size : 0; }
inline int rsize() const { return r ? r->size : 0; }
inline ll lsum() const { return l ? l->sum : 0; }
inline ll rsum() const { return r ? r->sum : 0; }
inline void pushup() {
size = lsize() + rsize() + 1;
sum = lsum() + rsum() + x;
}
} pool[Maxn * 20], *cur_ptr = pool;
inline node *newnode(int x, node *l = NULL, node *r = NULL) {
return &(*cur_ptr++ = node(x, l, r));
}
node *merge(node *l, node *r) {
if (!l || !r) return l ? l : r;
if (l->fix < r->fix) {
l->r = merge(l->r, r);
l->pushup();
return l;
}
else {
r->l = merge(l, r->l);
r->pushup();
return r;
}
}
void split(node *p, int k, node *&l, node *&r) {
if (!p) l = r = NULL;
else {
if (p->x <= k) {
l = p;
split(p->r, k, l->r, r);
l->pushup();
}
else {
r = p;
split(p->l, k, l, r->l);
r->pushup();
}
}
}
struct adjacent {
node *root;
int beg;
ll all_sum, inner_sum;
adjacent() = default;
inline void update_all() {
all_sum = inner_sum;
if (root) all_sum += root->sum * beg;
}
void insert(int b) {
node *A, *B;
split(root, b, A, B);
if (A) inner_sum += A->sum;
if (B) inner_sum += 1LL * B->size * b;
root = merge(merge(A, newnode(b)), B);
update_all();
}
void insert_treap(node *p) {
if (!p) return ;
insert(p->x);
insert_treap(p->l);
insert_treap(p->r);
}
friend adjacent operator + (adjacent x, adjacent y) {
int xsize = x.root ? x.root->size : 0;
int ysize = y.root ? y.root->size : 0;
if (xsize < ysize) swap(x, y);
x.beg = min(x.beg, y.beg);
x.insert_treap(y.root);
x.update_all();
return x;
}
};
adjacent adj[Maxn];
int fa[Maxn], sz[Maxn];
inline int fnd(int x) {
return fa[x] == x ? x : fa[x] = fnd(fa[x]);
}
void unite(int x, int y) {
x = fnd(x), y = fnd(y);
if (x == y) return;
if (sz[x] < sz[y]) swap(x, y);
fa[y] = x, sz[x] += sz[y];
after -= adj[x].all_sum;
after -= adj[y].all_sum;
adj[x] = adj[x] + adj[y];
after += adj[x].all_sum;
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> n;
before = 0;
set<int> rest;
for (int i = 0; i < Maxn; ++i) {
rest.insert(i);
fa[i] = i, sz[i] = 1;
}
for (int i = 1; i <= n; ++i) {
int a, b;
cin >> a >> b;
before += 1LL * a * b;
int pos = *rest.lower_bound(a);
rest.erase(pos);
adj[pos].beg = pos;
adj[pos].insert(b);
after += adj[pos].all_sum;
if (rest.find(pos - 1) == rest.end()) unite(pos - 1, pos);
if (rest.find(pos + 1) == rest.end()) unite(pos + 1, pos);
cout << after - before << endl;
}
return 0;
}
标签:time 应该 否则 要求 git pool distinct 生成树 queue
原文地址:https://www.cnblogs.com/libra9z/p/12445778.html