A 序列
题目大意 : 写一个程序,支持区间加,区间取max,单点询问值和修改过的次数(数值改变才算修改)
- 挺裸的吉司机线段树,考试时写假了
Code
Show Code
#include <cstdio>
#include <algorithm>
#define ls (rt << 1)
#define rs (rt << 1 | 1)
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
int n, a[N];
struct Tree {
ll m, s, ta; //m -> mininum, s -> second mininum, ta -> tag_add
int na, nc; //na -> number of times to add, nc -> number of times to modifications
}t[N*4];
void Pushup(int rt) {
if (t[ls].m == t[rs].m) t[rt].m = t[ls].m, t[rt].s = min(t[ls].s, t[rs].s);
else if (t[ls].m < t[rs].m) t[rt].m = t[ls].m, t[rt].s = min(t[ls].s, t[rs].m);
else t[rt].m = t[rs].m, t[rt].s = min(t[ls].m, t[rs].s);
}
void Build(int rt, int l, int r) {
if (l == r) return t[rt] = (Tree) {a[l], (ll)1e15}, void();
int mid = l + r >> 1;
Build(ls, l, mid); Build(rs, mid+1, r);
Pushup(rt);
}
void Update(int rt, ll w, int nc) {
if (t[rt].m < w) t[rt].m = w, t[rt].nc += nc;
}
void Pushdown(int rt) {
if (t[rt].na) {
ll ta = t[rt].ta; int na = t[rt].na; t[rt].ta = t[rt].na = 0;
t[ls].m += ta; t[ls].s += ta; t[ls].ta += ta; t[ls].na += na;
t[rs].m += ta; t[rs].s += ta; t[rs].ta += ta; t[rs].na += na;
}
if (t[rt].nc) {
ll m = t[rt].m; int nc = t[rt].nc; t[rt].nc = 0;
if (t[ls].m < m) t[ls].m = m, t[ls].nc += nc;
if (t[rs].m < m) t[rs].m = m, t[rs].nc += nc;
}
}
void Add(int rt, int l, int r, int x, int y, int w) {
if (x <= l && r <= y) return t[rt].m += w, t[rt].s += w, t[rt].ta += w, t[rt].na++, void();
int mid = l + r >> 1; Pushdown(rt);
if (x <= mid) Add(ls, l, mid, x, y, w);
if (y > mid) Add(rs, mid+1, r, x, y, w);
Pushup(rt);
}
void Change(int rt, int l, int r, int x, int y, int w) {
if (w <= t[rt].m) return;
if (x <= l && r <= y && w < t[rt].s) {
if (t[rt].m < w) t[rt].m = w, t[rt].nc++;
return;
}
int mid = l + r >> 1; Pushdown(rt);
if (x <= mid) Change(ls, l, mid, x, y, w);
if (y > mid) Change(rs, mid+1, r, x, y, w);
Pushup(rt);
}
int Ask(int rt, int l, int r, int x) {
if (l == r) return rt;
int mid = l + r >> 1; Pushdown(rt);
if (x <= mid) return Ask(ls, l, mid, x);
else return Ask(rs, mid + 1, r, x);
}
int main() {
n = read();
for (int i = 1; i <= n; ++i) a[i] = read();
Build(1, 1, n);
for (int m = read(); m; --m) {
char od; scanf(" %c", &od);
if (od == 'A') {
int l = read(), r = read(), w = read();
if (w) Add(1, 1, n, l, r, w);
}
else if (od == 'M') {
int l = read(), r = read();
Change(1, 1, n, l, r, read());
}
else {
int rt = Ask(1, 1, n, read());
printf("%lld %d\n", t[rt].m, t[rt].na + t[rt].nc);
}
}
return 0;
}
B 旅行计划
题目大意 :给定一个带权无向图,每次询问两点间路径权值和 mod K 的最小值,可重复经过点和边
-
对于 K 为奇数的部分如果联通就直接大力 puts("0") 就好了,因为只要存在一条路径,来回一共走 K 次就一定可以把权值和 mod K 变成 0
-
正解的话先算出这个联通块里所有边与 K 的 GCD g,当 k/g 是奇数的时候就来回走 k/g 次可以把答案变为 0
-
如果 k/g 是偶数,两点间距离是偶数也一定可以构造出 0 的情况,证明一下,我们现在可以走任意边偶数次并回到原点,那么这部分的权值和就是 \(\sum _i 2x_id_i\),现在所有的边和 K 都除掉他们的GCD后,由扩展裴蜀定理可知 \(\sum _i x_id_i\equiv 1\mod K\) ,所以 \(\sum _i 2x_id_i\equiv 2\mod K\),这样的话就一定可以构造出mod k值为2的路径, 走这种路径 ((k-x)mod k)/2 次就可以构造出0
-
如果联通块里有奇环的话也可以构造出 0,原因是从起点经过奇环再回到起点再走奇数长度到终点,这样长度就是奇数+偶数+奇数=偶数,可以把这个当作偶数路径,然后就套用上一条的结论就能构造出0了
-
如果上面的情况都没有的话那最小的可以构造出值为 g 的答案
Code
Show Code
#include <cstdio>
using namespace std;
const int N = 1e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
struct Edge {
int n, t, d;
}e[N*2];
int h[N], edc;
void Add(int x, int y, int z) {
e[++edc] = (Edge) {h[x], y, z}; h[x] = edc;
}
bool v[N];
int n, m, q, f[N], g[N], c[N];
int Find(int x) {
return x == f[x] ? x : f[x] = Find(f[x]);
}
int Gcd(int a, int b) {
return b ? Gcd(b, a % b) : a;
}
void Dfs(int x, int col) {
int f = Find(x); c[x] = col;
for (int i = h[x]; i; i = e[i].n) {
int y = e[i].t, d = e[i].d / g[f];
if (!c[y]) Dfs(y, col ^ (d & 1));
else if ((col ^ (d & 1)) != c[y]) v[f] = 1;
}
}
int main() {
n = read(); m = read(); q = read();
for (int i = 1; i <= n; ++i) f[i] = i;
while (m--) {
int x = read(), y = read(), z = read();
Add(x, y, z); Add(y, x, z);
x = Find(x), y = Find(y);
if (x == y) g[x] = Gcd(g[x], z);
else f[y] = x, g[x] = Gcd(g[x], Gcd(g[y], z));
}
for (int i = 1; i <= n; ++i)
if (!c[i]) Dfs(i, 2);
while (q--) {
int x = read(), y = read(), k = read();
int fx = Find(x), fy = Find(y), gcd = Gcd(k, g[fx]);
if (fx != fy) puts("NIE");
else if ((k / gcd) & 1 || c[x] == c[y] || v[fx]) puts("0");
else printf("%d\n", gcd);
}
return 0;
}
C Hack
题目大意 : 一个带权有向图,删去一些边保证从0不能走到n-1,并且任意一条路径上不能有两条边被删
- 如果没有说一条路径上只能删一条边的限制就可以直接最小流了,而有这个限制的话就给反向边建inf,然后给能dfs到的点跑个最小割就可以了(其实是没懂的)
Code
Show Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 105;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
struct Edge {
int n, t; ll d;
}e[N*N>>1];
int h[N], edc = 1;
void Add(int x, int y, ll z) {
e[++edc] = (Edge) {h[x], y, z}; h[x] = edc;
}
ll ans;
bool v[N];
int n, dep[N], q[N], l, r, tmp[N];
void Dfs(int x) {
v[x] = 1;
for (int i = h[x], y; i; i = e[i].n)
if (!v[y=e[i].t] && e[i].d != 1e12) Dfs(y);
}
bool Bfs() {
memset(dep + 1, 0, n * 4);
memcpy(h + 1, tmp + 1, n * 4);
dep[1] = 1; q[l=r=1] = 1;
while (l <= r) {
int x = q[l++];
for (int i = h[x], y; i; i = e[i].n) {
if (!v[y=e[i].t] || !e[i].d || dep[y]) continue;
dep[y] = dep[x] + 1; q[++r] = y;
if (y == n) return 1;
}
}
return 0;
}
ll Dinic(int x, ll lim) {
if (x == n) return lim;
ll sum = 0;
for (int i = h[x]; i && lim; i = e[i].n) {
int y = e[i].t; h[x] = i;
if (!e[i].d || dep[y] != dep[x] + 1) continue;
ll f = Dinic(y, min(lim, e[i].d));
sum += f; lim -= f;
e[i].d -= f; e[i^1].d += f;
}
if (!sum) dep[x] = 0;
return sum;
}
int main() {
n = read();
for (int m = read(); m; --m) {
int x = read() + 1, y = read() + 1, z = read();
Add(x, y, z); Add(y, x, 1e12);
}
Dfs(1);
if (!v[n]) return puts("-1"), 0;
memcpy(tmp + 1, h + 1, n * 4);
while (Bfs()) ans += Dinic(1, 1e12);
printf("%lld\n", ans >= 1e12 ? -1ll : ans);
return 0;
}