Blogs2
Posted nishikino-curtis
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Blogs2相关的知识,希望对你有一定的参考价值。
始终做完全公开的博客
单调栈,前缀和,RMQ。
Description
给定长度为(n)的序列:(a_1,a_2,ldots ,a_n),记为(a[1:n])。类似地,(a[l:r](1le l le r le N))是指序列:(a_l,a_{l+1},ldots , a_{r-1},a_r)。若(1 le l le s le t le r le n),则称(a[s:t])是(a[l:r])的子序列。
现在有(q)个询问,每个询问给定两个数(l)和(r),(1 le l le r le n),求(a[l:r])的不同子序列的最小值之和。
例如,给定序列(5,2,4,1,3),询问给定的两个数为(1和3),那么(a[1:3])有
6个子序列(a[1:1],a[2:2],a[3:3],a[1:2],a[2:3],a[1:3]),这6个子序列的最小值之和为(5+2+4+2+2+2=17)。
Solution
首先有一个很常见的套路,我们不能直接统计就算贡献。
对于一个区间中的最小值 (a_p), 所有跨越她的区间有((r - p + 1) imes (p - l + 1))个, 可以加入贡献, 因此我们接着要计算的就是区间([l, p))和((p, r])的答案.
但这样做下去单次最好也要(O(n)).
我们定义一个(F[l][r]), 为右端点在(r), 左端点在([l,r])的所有答案, 对于右侧的情况, 我们需要得到的就是(F[p + 1][r]). 首先考虑(F)的递推关系, 定义(pre_r)为(r)之前第一个小于她的位置, 那么在这之后(a_r)都是区间内最小值, 也就是: (F[l][r] = F[l][pre_r] + a_r imes (r - pre_r)).
这是可以递推的! 但由于状态量是(n^2)的, 我们还需要做一些优化.
考虑丢掉(l), 定义一个状态(f_p), 归纳可得:
由于(p)是区间内最小值, 所以向前推若干位时, 一定会存在一个(x), 使得(pre_x = p), 那么对于一个(r), 就有(f_r = a_r imes (r - pre_r) + a_{pre_r} imes(ldots)+ldots + a_x imes (x - p) + f_p).
这时我们有(f_r - f_p)为右端点在(r), 左端点在((p, r])的答案.
那么我们就可以得出每个右端点(r_i in (p,r])的答案了, 利用前缀和优化, 有:
$ egin{align}ans = sum_{i = p + 1}^{r}{f_i - f_p} = sum_{i=p+1}^{r}{f_i} - {f_p imes (r - p)} = sum_r - sum_p - f_p imes (r - p)end{align}$.
左侧的答案同理,反向求一遍即可。
单调栈可以(O(n))求出每个位置的(pre)和(suf), 每次询问用ST表查出最小值位置,前后两端求一下答案就好了。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn = 1e+5 + 5;
int n, q;
int a[maxn], pre[maxn], suf[maxn];
int stk[maxn], top;
int ST[maxn][18], LO2[maxn], PO2[20];
ll ftr[maxn], ftl[maxn], sfr[maxn], sfl[maxn];
inline int rd() {
register int x = 0, f = 0, c = getchar();
while (!isdigit(c)) {
if (c == '-') f = 1;
c = getchar();
}
while (isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
return f ? -x : x;
}
inline int query(int l, int r) {
int delt = LO2[r - l + 1];
return (a[ST[l][delt]] < a[ST[r - PO2[delt] + 1][delt]] ?
ST[l][delt] :
ST[r - PO2[delt] + 1][delt]);
}
int main() {
n = rd(); q = rd();
LO2[1] = 0;
for (int i = 2; i <= n; ++i) LO2[i] = (LO2[i >> 1] + 1);
PO2[0] = 1;
for (int i = 1; i < 18; ++i) PO2[i] = (PO2[i - 1] << 1);
for (int i = 1; i <= n; ++i) {
a[i] = rd(); ST[i][0] = i;
}
for (int j = 1; j <= LO2[n]; ++j)
for (int i = 1; i <= n - PO2[j - 1] + 1; ++i)
ST[i][j] = (a[ST[i][j - 1]] < a[ST[i + PO2[j - 1]][j - 1]] ? ST[i][j - 1] : ST[i + PO2[j - 1]][j - 1]);
a[0] = a[n + 1] = 0x3f3f3f3f;
for (int i = 1; i <= n; ++i) {
while (top && a[stk[top]] > a[i]) suf[stk[top]] = i, top--;
pre[i] = stk[top]; stk[++top] = i;
}
while (top) pre[stk[top]] = stk[top - 1], suf[stk[top]] = n + 1, top--;
for (int i = 1; i <= n; ++i)
ftr[i] = ftr[pre[i]] + (ll)a[i] * (i - pre[i]), sfr[i] = sfr[i - 1] + ftr[i];
for (int i = n; i; --i)
ftl[i] = ftl[suf[i]] + (ll)a[i] * (suf[i] - i), sfl[i] = sfl[i + 1] + ftl[i];
int l, r, pos;
while (q--) {
l = rd(); r = rd(); pos = query(l, r);
ll ans = 1ll * a[pos] * (pos - l + 1) * (r - pos + 1) +
(ll)sfr[r] - (ll)sfr[pos] - 1ll * ftr[pos] * (r - pos) +
(ll)sfl[l] - (ll)sfl[pos] - 1ll * ftl[pos] * (pos - l);
printf("%lld
", ans);
}
return 0;
}
然后听说有一道类似的题BZOJ4262
线段树 + 树状数组(什么鬼, 我选择单调栈)
Description
给定一个序列,初始为空。现在我们将$1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置。每插入一个数字,我们都想知道此时最长上升子序列长度是多少?
Solution
我们可以发现一个性质, 由于数字是递增插入的, 所以不会影响插入位置后面的答案. 那么我们直接构造出最终的序列, 求出每个位置对应数值的答案(f_{a[i]}), 那么到每个数值为止的答案就是(f)的前缀最大值了.
怎么构造最终序列呢? 平衡树?算了早就不会写了可以用线段树倒着占坑, 因为后插入的不会影响先插入的相对位置, 也就是我们倒着插入时找到第(x_i + 1)个空位就可以了, 在线段树上二分即可.
然后就是求LIS了, 随便哪种(O(nlogn))的算法都可以.
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 1e+5 + 5;
int n, a[maxn];
int stk[maxn], mx, top, ans[maxn];
int s[maxn << 2];
inline void pushup(int cur) {
s[cur] = s[cur << 1] + s[cur << 1|1];
}
void build(int cur, int l, int r) {
if (l == r) {
s[cur] = 1; return;
}
int mid = (l + r) >> 1;
build(cur << 1, l, mid);
build(cur << 1|1, mid + 1, r);
pushup(cur);
}
void update(int cur, int l, int r, int p, int v) {
if (l == r) {
s[cur] = v;
return;
}
int mid = (l + r) >> 1;
if (p <= mid) update(cur << 1, l, mid, p, v);
else update(cur << 1|1, mid + 1, r, p, v);
pushup(cur);
}
int query(int cur, int l, int r, int k) {
if (l == r) {
return l;
}
int mid = (l + r) >> 1, tmp = s[cur << 1];
if (k <= tmp) return query(cur << 1, l, mid, k);
else return query(cur << 1|1, mid + 1, r, k - tmp);
}
int main()
{
scanf("%d", &n); int *x = new int[n + 1];
build(1, 1, n);
for (int i = 1; i <= n; ++i)
scanf("%d", x + i);
for (int i = n; i; --i) {
//scanf("%d", x);
int pos = query(1, 1, n, *(x+i) + 1);
a[pos] = i;
update(1, 1, n, pos, 0);
}
for (int i = 1; i <= n; ++i) {
if (stk[top] < a[i]) stk[++top] = a[i], ans[a[i]] = top;
else {
int pos = lower_bound(stk + 1, stk + 1 + top, a[i]) - stk;
stk[pos] = a[i];
ans[a[i]] = pos;
}
}
for (int i = 1; i <= n; ++i) {
ans[i] = max(ans[i], ans[i - 1]);
printf("%d
", ans[i]);
}
return 0;
}
拆点, 矩阵乘法,路径计数。
Description
给定一张n个点m条边的带权有向图,每条边的边权只可能是1,2,3中的一种。
将所有可能的路径按路径长度排序,请输出第k小的路径的长度,注意路径不一定是简单路径,即可以重复走同一个点。
Solution
看到巨大的数据范围就会想到log级别的算法了,此题点数不多,矩阵乘法是一个选择。
实际操作可以类比一道边权在10以内的题,我们也可以按单位距离拆点,然后进行路径计数,恰好等于K时走了多少步,就是答案长度了。
写起来有很多细节,而且计数很容易爆long long,膜了GXZLegend的题解,可以绑一个计数器到各个点,然后用倍增的方式,逐次累加答案并构造出答案的每个二进制位。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxp = 121;
const int maxn = 41;
typedef long long ll;
int n, m, lim;
ll k, ans;
struct matrix
{
ll val[maxp][maxp];
matrix() {
memset(val, 0, sizeof val);
}
ll& operator () (int x, int y) {
return val[x][y];
}
matrix operator * (matrix b) {
matrix ret;
for (int k = 0; k <= lim; ++k)
for (int i = 0; i <= lim; ++i)
for (int j = 0; j <= lim; ++j)
ret(i, j) += val[i][k] * b(k, j);
return ret;
}
bool check() {
ll count = 0;
for (int i = 1; i <= n; ++i) {
count += (val[i][0] - 1);
if (count >= k) return 1;
}
return 0;
}
}dou[70], res;
int main() {
int u, v, w;
scanf("%d%d%lld", &n, &m, &k);
dou[0](0,0) = 1; lim = 3 * n;
for (int i = 1; i <= n; ++i)
res(i, i) = dou[0](i, 0) = dou[0](i, i + n) = dou[0](i + n, i + 2 * n) = 1;
for (int i = 1; i <= m; ++i) {
scanf("%d%d%d", &u, &v, &w);
dou[0](u + (w - 1) * n, v)++;
}
int tim = 1;
for(; ; ++tim) {
if (tim > 65) {
puts("-1");
return 0;
}
dou[tim] = dou[tim - 1] * dou[tim - 1];
if (dou[tim].check()) break;
}
tim -= 1;
for (; tim >= 0; --tim) {
matrix tmp = res * dou[tim];
if (!tmp.check()) res = tmp, ans |= (1ll << tim);
}
printf("%lld
", ans);
return 0;
}
A Set Of Fun Problems II
两道洛谷上的题。
Luogu P2425
Description
小红帽喜欢回文数,但生活中的数常常不是回文数。
现在她手上有(t)个数,现在她知道这(t)个数分别在(x)进制下是回文数((x ge 2)),请你对于每个数求出最小的(x).
Solution
首先, 对于一个数(n), (n+1) 一定是一个回文数, 因为她在该进制下只有一位.
对于小于(sqrt{n})的进制, 我们可以暴力枚举.
对于大于(sqrt{n})的进制, 我们知道她一定只有两位, 分别是(n div x)和(n,mod,x) , 如果她是回文的, (n)就可以表示成(p imes x + p)的形式, 也就是(p(x + 1) = n), 所以我们可以找一个最大的(p), 使得(p le sqrt{n})且(p |n). 这样的枚举也是(O(sqrt{n}))的, 找到一个以后, 答案就是(n div p - 1)了.
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
const ll inf = 10000000000;
ll a, arr[1005], len;
bool chk(ll d) {
bool sol = true;
ll tmp = a;
len = 0;
while (tmp) {
arr[len++] = tmp % d;
tmp /= d;
}
int mid = len / 2;
for (int i = 0; i <= mid; ++i) {
int j = len - i - 1;
if (arr[i] != arr[j]) {
sol = false; break;
}
}
return sol;
}
int main() {
int T; scanf("%d", &T);
while (T--) {
scanf("%lld", &a);
if (a == 1 || a == 3) {
puts("2");
continue;
} else if (a == 2) {
puts("3");
continue;
}
ll pos = inf;
ll lim = sqrt(a) + 1;
for (int i = 2; i <= lim; ++i) {
if (chk(i)) {
pos = i; break;
}
}
if (pos < inf) {
printf("%lld
", pos);
continue;
}
for (ll i = a / lim - 1; i; --i) {
if (a == a / i * i) {
pos = a / i - 1;
break;
}
}
if (pos < inf) printf("%lld
", pos);
else printf("%lld
", a + 1);
}
return 0;
}
Luogu P1875
Description
给定一些合成关系, 和直接购买的费用, 求得到一个东西的最小代价和方案数.
Solution
听说树形DP能过, 不过没什么意义. 因为题目并没有保证没有环出现.
我们用最短路的过程来消去DP的后效性.
考虑Dijkstra的过程, 当一个点从堆里被拿出来的时候, 就可以确定她是最小值了. 那么我们的更新就建立再已经求出的最小值上, 并且只用最小值向下更新, 至于计数就是套路了, 都知道怎么做.
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn = 1005;
struct node
{
int d, id;
node(int d_ = 0, int i_ = 0):d(d_), id(i_) {}
bool operator < (const node &rhs) const {
return d > rhs.d;
}
};
int n, tow[maxn][maxn], cost[maxn];
int f[maxn], tag[maxn], ans[maxn];
priority_queue<node> q;
int main() {
scanf("%d", &n);
for (int i = 0; i < n; ++i)
scanf("%d", &cost[i]), q.push(node(cost[i], i)), ans[i] = 1;
int x, y, z;
memset(tow, -1, sizeof tow);
while (scanf("%d%d%d", &x, &y, &z) != EOF) {
tow[x][y] = z;
tow[y][x] = z;
}
while (!q.empty()) {
node u = q.top(); q.pop();
if (tag[u.id]) continue;
//printf("%d
", u.id);
tag[u.id] = 1;
for (int i = 0; i < n; ++i) {
if (tag[i] && ~tow[u.id][i]) {
//printf("%d %d %d
", u.id, i, tow[u.id][i]);
if (cost[u.id] + cost[i] < cost[tow[u.id][i]])
cost[tow[u.id][i]] = cost[u.id] + cost[i],
ans[tow[u.id][i]] = ans[u.id] * ans[i],
q.push(node(cost[tow[u.id][i]], tow[u.id][i]));//,printf("+%d
", tow[u.id][i]);
else if (cost[u.id] + cost[i] == cost[tow[u.id][i]])
ans[tow[u.id][i]] += ans[u.id] * ans[i],
q.push(node(cost[tow[u.id][i]], tow[u.id][i]));//,printf("s%d
", ans[tow[u.id][i]]);
}
}
}
printf("%d %d
", cost[0], ans[0]);
return 0;
}
树链剖分。
Description
有一张无向图, 定义关键路径为删去后图不连通的路径, 现在要依次删去一些边, 随时有一些询问, 求两点间的关键路径数.
Solution
这种题首先要想到倒序处理, 因为删边不好做.
然后来考虑题目的性质, 加入这张图最后被删得只剩一颗树了, 那么每一次加边, 都会让边两端路径上的关键路径变为0.
而在一颗树上两点之间关键路径数都是1.
区间求和, 修改? 树链剖分啊.
首先删边, 然后把剩下的图跑出来一颗生成树, 再用非树边更新, 接着依次执行操作和询问即可.
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cctype>
using namespace std;
const int maxn = 30005;
const int maxm = 1e+5 + 5;
struct option
{
int typ, a, b;
}q[maxn + 10005];
struct edge
{
int to, nxt; bool ban;
}e[maxm << 1];
int n, m, ptr, lnk[maxn], opn, cnt;
int dfn[maxn], top[maxn], f[maxn], mxson[maxn], siz[maxn];
int ans[maxn], dep[maxn];
inline void add(int bgn, int end) {
e[ptr] = (edge) {end, lnk[bgn], 0};
lnk[bgn] = ptr; ptr++;
e[ptr] = (edge) {bgn, lnk[end], 0};
lnk[end] = ptr; ptr++;
}
inline int rd() {
register int x = 0, f = 0, c = getchar();
while (!isdigit(c)) {
if (c == '-') f = 1;
c = getchar();
}
while (isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
return f ? -x : x;
}
void dfs(int x, int fa) {
//printf("%d
", x);
siz[x] = 1;
f[x] = fa;
dep[x] = dep[fa] + 1;
for (int p = lnk[x]; ~p; p = e[p].nxt) {
int y = e[p].to;
if (y == fa || e[p].ban || dep[y]) continue;
dfs(y, x);
siz[x] += siz[y];
if (siz[y] > siz[mxson[x]]) mxson[x] = y;
}
}
void dfs2(int x, int init) {
dfn[x] = ++cnt;
top[x] = init;
if (!mxson[x]) return;
dfs2(mxson[x], init);
for (int p = lnk[x]; ~p; p = e[p].nxt) {
int y = e[p].to;
if (f[y] != x || y == mxson[x] || e[p].ban) continue;
dfs2(y, y);
}
}
int s[maxn << 2], tag[maxn << 2];
inline void pushup(int cur) {
s[cur] = s[cur << 1] + s[cur << 1|1];
}
inline void pushdown(int cur) {
if (tag[cur]) {
s[cur << 1] = s[cur << 1|1] = 0;
tag[cur << 1] = tag[cur << 1|1] = 1;
tag[cur] = 0;
}
}
void build(int cur, int l, int r) {
if (l == r) {
s[cur] = 1;
return;
}
int mid = (l + r) >> 1;
build(cur << 1, l, mid);
build(cur << 1|1, mid + 1, r);
pushup(cur);
}
void update(int cur, int l, int r, int L, int R) {
if (L <= l && r <= R) {
s[cur] = 0;
tag[cur] = 1;
return;
}
int mid = (l + r) >> 1; pushdown(cur);
if (L <= mid) update(cur << 1, l, mid, L, R);
if (R > mid) update(cur << 1|1, mid + 1, r, L, R);
pushup(cur);
}
int query(int cur, int l, int r, int L, int R) {
if (L <= l && r <= R) return s[cur];
int mid = (l + r) >> 1, ret = 0; pushdown(cur);
if (L <= mid) ret += query(cur << 1, l, mid, L, R);
if (R > mid) ret += query(cur << 1|1, mid + 1, r, L, R);
return ret;
}
void updt(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
update(1, 1, n, dfn[top[x]], dfn[x]);
x = f[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
if (dep[x] != dep[y]) update(1, 1, n, dfn[x] + 1, dfn[y]);
}
void efs(int x) {
//printf("e%d
", x);
for (int p = lnk[x]; ~p; p = e[p].nxt) {
int y = e[p].to;
if (e[p].ban) continue;
if (f[y] == x) efs(y);
if (y != f[x] && dep[y] < dep[x])
updt(x, y);
}
}
int queryt(int x, int y) {
int ret = 0;
//printf("%d %d
", x, y);
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
//printf("q %d %d
", dfn[top[x]], dfn[x]);
ret += query(1, 1, n, dfn[top[x]], dfn[x]);
x = f[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
//printf("q %d %d
", dfn[x], dfn[y]);
ret += query(1, 1, n, dfn[x], dfn[y]);
ret -= query(1, 1, n, dfn[x], dfn[x]);
return ret;
}
int main() {
n = rd(); m = rd();
memset(lnk, -1, sizeof lnk);
int a, b;
for (int i = 1; i <= m; ++i) {
a = rd(); b = rd();
add(a, b);
}
int c;
while (true) {
c = rd(); if (c == -1) break;
q[++opn].typ = c;
q[opn].a = rd(); q[opn].b = rd();
if (!c) {
for (int p = lnk[q[opn].a]; ~p; p = e[p].nxt) {
if (e[p].to == q[opn].b) {
//printf("%d %d
", e[p].to, q[opn].b);
//puts("try");
e[p].ban = e[p ^ 1].ban = 1;
break;
}
}
}
}
dfs(1, 0);
dfs2(1, 1);
build(1, 1, n);
efs(1);
for (int i = opn; i; --i) {
//puts("try");
if (q[i].typ) {
ans[i] = queryt(q[i].a, q[i].b);
} else {
updt(q[i].a, q[i].b);
}
}
for (int i = 1; i <= opn; ++i) {
if (q[i].typ) printf("%d
", ans[i]);
}
return 0;
}
拓扑排序 + 最短路。
Description
有一些正权双向边和边权可能为负的单向边, 保证没有负环. 求最短路, 且不能使用SPFA.
Solution
不能使用SPFA就只能用Dijkstra了, 但是她不能处理负权边, 注意到没有负环, 也就是把图缩点后一定是负权边在DAG上转移, 假如我们每次只做联通块内的点, Dijkstra是没有影响的.
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <queue>
using namespace std;
const int maxn = 25005;
const int maxm = 100005;
struct edge
{
int to, nxt, v;
}e[maxm << 1], ce[maxm];
struct node
{
int d, id;
node(int d_ = 0, int i_ = 0):d(d_),id(i_){}
bool operator < (const node &rhs) const {
return d > rhs.d;
}
};
int cptr, clnk[maxn];
int ptr, t, lnk[maxn], dis[maxn], n, m1, m2, s, dgr[maxn];
int dfn[maxn], cnt, num, bel[maxn], low[maxn], vis[maxn];
stack<int> st;
vector<int> vec[maxn];
inline int rd() {
register int x = 0, f = 0, c = getchar();
while (!isdigit(c)) {
if (c == '-') f = 1;
c = getchar();
}
while (isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
return f ? -x : x;
}
inline void add(int bgn, int end, int val) {
e[++ptr] = (edge){end, lnk[bgn], val};
lnk[bgn] = ptr;
}
inline void cadd(int bgn, int end, int val) {
ce[++cptr] = (edge){end, clnk[bgn], val};
clnk[bgn] = cptr;
}
void tarjan(int x) {
dfn[x] = low[x] = ++cnt;
vis[x] = 1;
st.push(x);
for (int p = lnk[x]; p; p = e[p].nxt) {
int y = e[p].to;
if (!dfn[y]) {
tarjan(y);
low[x] = min(low[x], low[y]);
} else if (vis[y]) {
low[x] = min(low[x], dfn[y]);
}
}
if (dfn[x] == low[x]) {
int now; num++;
do {
now = st.top(); st.pop();
bel[now] = num; vec[num].push_back(now);
vis[now] = 0;
} while (now != x);
}
}
void dijkstra(int x) {
priority_queue<node> q;
for (vector<int>::iterator iter = vec[x].begin(); iter != vec[x].end(); ++iter)
if (dis[*iter] < 0x3f3f3f3f) q.push(node(dis[*iter], *iter));
while (!q.empty()) {
node u = q.top(); q.pop();
if (vis[u.id]) continue;
vis[u.id] = 1;
for (int p = lnk[u.id]; p; p = e[p].nxt) {
int y = e[p].to;
if (dis[y] > dis[u.id] + e[p].v) {
dis[y] = dis[u.id] + e[p].v;
if (bel[u.id] != bel[y]) continue;
q.push(node(dis[y], y));
}
}
}
}
void toposort() {
queue<int> q;
for (int i = 1; i <= num; ++i)
if (!dgr[i]) q.push(i);
while (!q.empty()) {
int u = q.front(); q.pop();
dijkstra(u);
for (int p = clnk[u]; p; p = ce[p].nxt) {
int y = ce[p].to;
if (!--dgr[y]) q.push(y);
}
}
}
int main() {
n = rd(); m1 = rd(); m2 = rd(); s = rd();
int u, v, w;
memset(dis, 0x3f, sizeof dis);
dis[s] = 0;
for (int i = 1; i <= m1; ++i) {
u = rd(); v = rd(); w = rd();
add(u, v, w);
add(v, u, w);
}
for (int i = 1; i <= m2; ++i) {
u = rd(); v = rd(); w = rd();
add(u, v, w);
}
for (int i = 1; i <= n; ++i)
if (!dfn[i]) tarjan(i);
for (int i = 1; i <= n; ++i) {
for (int p = lnk[i]; p; p = e[p].nxt) {
int y = e[p].to;
if (bel[i] != bel[y])
cadd(bel[i], bel[y], e[p].v), ++dgr[bel[y]];
}
}
toposort();
for (int i = 1; i <= n; ++i) {
if (dis[i] < 0x3f3f3f3f) printf("%d
", dis[i]);
else puts("NO PATH");
}
return 0;
}
线段树。
Description
维护一个01序列, 支持查询区间1的个数, 区间置0, 取出区间里所有的1并填入某个区间, 优先填靠前的0, 多了就浪费.
Solution
首先看见区间操作肯定要线段树的. 区间查询和置0都好做, 主要是操作3.
操作3的第一步是取出1, 这个和区间查询同理.
如果发现要浪费, 就直接区间置1.
关键就在于每次找一段最长0了, 这个可以二分啦. 有时区间头是1, 那么我们还需要跳过一段最长1. 这两个二分都可以做.
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cctype>
using namespace std;
const int maxn = 2e+5 + 5;
int n, m;
int s[maxn << 2], tag[maxn << 2];
inline void pushup(int cur) {
s[cur] = s[cur << 1] + s[cur << 1|1];
}
inline void pushdown(int cur, int len) {
if (~tag[cur]) {
s[cur << 1] = tag[cur] * (len - (len >> 1));
s[cur << 1|1] = tag[cur] * (len >> 1);
tag[cur << 1] = tag[cur << 1|1] = tag[cur];
tag[cur] = -1;
}
}
void build(int cur, int l, int r) {
tag[cur] = -1;
if (l == r) {
s[cur] = 1;
return;
}
int mid = (l + r) >> 1;
build(cur << 1, l, mid);
build(cur << 1|1, mid + 1, r);
pushup(cur);
}
void update(int cur, int l, int r, int L, int R, int v) {
if (L <= l && r <= R) {
s[cur] = v * (r - l + 1);
tag[cur] = v;
return;
}
int mid = (l + r) >> 1; pushdown(cur, r - l + 1);
if (L <= mid) update(cur << 1, l, mid, L, R, v);
if (R > mid) update(cur << 1|1, mid + 1, r, L, R, v);
pushup(cur);
}
int querys(int cur, int l, int r, int L, int R) {
if (L <= l && r <= R) return s[cur];
int mid = (l + r) >> 1, ret = 0; pushdown(cur, r - l + 1);
if (L <= mid) ret += querys(cur << 1, l, mid, L, R);
if (R > mid) ret += querys(cur << 1|1, mid + 1, r, L, R);
return ret;
}
int query(int lb, int rb, int k) {
if (lb > rb) return -1;
int l = 0, r = rb - lb;
while (l <= r) {
int mid = (l + r) >> 1;
if (querys(1, 1, n, lb, lb + mid) == k * (mid + 1)) l = mid + 1;
else r = mid - 1;
}
return l;
}
inline int rd() {
register int x = 0, c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
return x;
}
int main() {
n = rd(); m = rd();
build(1, 1, n);
int opt, l1, r1, l2, r2;
while (m--) {
opt = rd();
if (opt == 0) {
l1 = rd(); r1 = rd();
update(1, 1, n, l1, r1, 0);
} else if (opt == 1) {
l1 = rd(); r1 = rd(); l2 = rd(); r2 = rd();
int s = querys(1, 1, n, l1, r1);
update(1, 1, n, l1, r1, 0);
int s_already = querys(1, 1, n, l2, r2);
if (s > (r2 - l2 + 1) - s_already) {
update(1, 1, n, l2, r2, 1); continue;
}
if (!s) continue;
while (true) {
int hed = 0;
if (querys(1, 1, n, l2, l2) == 1) {
hed = query(l2, r2, 1); l2 += hed;
}
hed = query(l2, r2, 0);
if (hed == -1) break;
if (hed > s) {
update(1, 1, n, l2, l2 + s - 1, 1);
break;
} else {
update(1, 1, n, l2, l2 + hed - 1, 1);
l2 += hed; s -= hed;
}
}
} else {
l1 = rd(); r1 = rd();
int ans = 0;
while (true) {
int hed = 0;
if (querys(1, 1, n, l1, l1) == 1) {
hed = query(l1, r1, 1); l1 += hed;
}
hed = query(l1, r1, 0);
if (hed == -1) break;
ans = max(ans, hed);
l1 += hed;
if (l1 > r1) break;
}
printf("%d
", ans);
}
}
return 0;
}
!-->!-->!-->!-->!-->!-->!-->!-->!-->!-->!-->!-->!-->!-->以上是关于Blogs2的主要内容,如果未能解决你的问题,请参考以下文章