板子3
最小树形图(朱刘算法)
int pre[maxn], col[maxn], id[maxn], in[maxn];
struct Edge {
int u, v, va;
} e[maxn*40];
int ZL_MST(int root, int nv, int tot) {
int rtans = 0;
while (1) {
for (int i = 1; i <= nv; i++) in[i] = INF;
for (int i = 1; i <= tot; i++) {
int u = e[i].u, v = e[i].v;
if (u != v && e[i].va < in[v]) {
pre[v] = u;
in[v] = e[i].va;
}
}
for (int i = 1; i <= nv; i++) {
if (i != root && in[i] == INF)
return -1;
}
int cnt = 0; in[root] = 0;
memset(col, -1, sizeof(col));
memset(id, -1, sizeof(id));
for (int i = 1; i <= nv; i++) {
rtans += in[i];
int v = i;
while (col[v] != i && id[v] == -1 && v != root) {
col[v] = i;
v = pre[v];
}
if (id[v] == -1 && v != root) {
id[v] = ++cnt;
for (int j = pre[v]; j != v; j = pre[j]) id[j] = cnt;
}
}
if (!cnt) break;
for (int i = 1; i <= nv; i++) if (id[i] == -1) id[i] = ++cnt;
for (int i = 1; i <= tot; i++) {
int v = e[i].v;
e[i].u = id[e[i].u];
e[i].v = id[e[i].v];
if (e[i].u != e[i].v)
e[i].va -= in[v];
}
nv = cnt;
root = id[root];
}
return rtans;
}
辗转相除的搞死小圆(带计算生成树计数)
ll g[maxn][maxn];
ll Ma_Tree(int len) {
ll ans = 1;
for (int i = 1; i <= len; i++) {
for (int j = 1; j <= len; j++) {
g[i][j] = (g[i][j] + mod) % mod;
}
}
for (int i = 1; i <= len; i++) {
for (int j = i + 1; j <= len; j++) {
ll a = g[i][i], b = g[j][i];
while (b) {
ll tmp = a / b; a %= b; swap(a, b);
for (int k = i; k <= len; k++) g[i][k] = (g[i][k] - g[j][k]*tmp + mod) % mod;
for (int k = i; k <= len; k++) swap(g[i][k], g[j][k]);
ans = -ans;
}
}
}
for (int i = 1; i <= len; i++) {
//if (g[i][i] == 0) return -1;
ans = ans * g[i][i] % mod;
}
if (ans < 0) ans += mod;
return ans;
}
用SPFA解需要注意的:
1.原图可能不是连通图,故需要加一个超级源点S,从S到任意的顶点边权为0,然后从该点出发。为什么?添加从虚点S到每个顶点的权为0的边.这是为了保证构造出来的图是连通的.由于虚点本身并不引入负圈,所以设置虚点以后最短路仍然存在,并且每个约束仍然满足.
或者差分约束不用什么附加顶点, 附加顶点的唯一用处就是保证图的连通性, 不让你有负环判不到的情况, 解决这种问题的最佳途径就是初始把所有顶点都加入队列, 并且将所有dis
置0, 这就相当于加了一个不存在的附加顶点, 它与所有的顶点的直连长度都是0.
当然推荐第二种,效率也高。
2.如果求最小的解,那么我们一开始把dis设为无穷小,并且使用最长路。即 if(d[v] < d[u]+w(u,v)) 进行更新,而建图的时候也要用大于等于。
如果求最大,那么我们一开始把dis设为无穷大,使用最短路。
为什么?
问得好!(- -|||) 以求解最大的为例(最小解同理)dis[id]一开始为无穷大,图最短路更新的条件为: if(d[v]>d[u]+w(u,v)) d[v]=d[u]+w(u,v); 通过不断的松弛,使得d的值不断变小,直到满足所有条件,也就是说满足条件的时候就是最大的了~
那么,我们建图的时,都转化为A-B<=C这种形式,以(B为起点,向A连接一条权值为C的边) 因为图的最短路有:d[v]- d[u]<=w(u,v); 即( d[v]<=d[u]+w(u,v); )
所以,当你在纠结用小于号大于号的时候,看看题目求的是最大还是最小,如果只是判断有木有解,那么大于号小于都可以,只不过要注意全部等式要统一。
差分约束系统(SPFA)(包括答案,都化成a - b <= c的形式)
int head[maxn], cnt, dis[maxn];
struct Edge {
int v, w, next;
} e[maxn*20];
void add(int u, int v, int w) {
e[cnt].v = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt++;
}
void init() {
cnt = 0;
memset(head, -1, sizeof(head));
}
bool spfa(int s, int len) {
bool vis[maxn] = {0};
int in[maxn] = {0};
queue<int> q;
for (int i = 1; i <= len; i++)
dis[i] = INF;
q.push(s);
vis[s] = true;
dis[s] = 0;
while (!q.empty()) {
int t = q.front(); q.pop();
vis[t] = false;
for (int i = head[t]; ~i; i = e[i].next) {
int v = e[i].v;
if (dis[v] > dis[t] + e[i].w) {
dis[v] = dis[t] + e[i].w;
if (!vis[v]) {
q.push(v);
vis[v] = true;
if (++in[v] > len) return false;
}
}
}
}
return true;
}
// 其中a - c <= ? 是求最短路
void solve() {
int n, x, y;
cin >> n >> x >> y;
init();
for (int i = 1; i <= x; i++) {
int a, b, c; scanf("%d%d%d", &a, &b, &c);
add(a, b, c); //b - a <= c
}
for (int i = 1; i <= y; i++) {
int a, b, c; scanf("%d%d%d", &a, &b, &c);
add(b, a, -c); // b - a >= c => a - b <= -c
}
if (!spfa(1, n)) cout << -1 << endl; //有环是无解
else {
if (dis[n] == INF) cout << -2 << endl; //到不了是多解,表示a和c没有关系
else cout << dis[n] << endl;
}
}
刘汝佳的Dinic(很快~)
int head[maxn], cnt;
int n, m, s, t;
bool vis[maxn];
int d[maxn], cur[maxn];
struct Edge {
int u, v;
int cap, flow;
} e[maxn*40]; //因为是双向边 所以记得开二倍
vector<int> G[maxn];
void init() {
memset(head, -1, sizeof(head));
for (int i = 0; i <= 200; i++) //一定不要对vector使用memset!!!!!
G[i].clear();
cnt = 0;
}
void add(int u, int v, int cap, int f) {
e[cnt].u = u;
e[cnt].cap = cap;
e[cnt].flow = f;
e[cnt].v = v;
}
void AddEdge(int u, int v, int cap) {
add(u, v, cap, 0);
G[u].push_back(cnt++);
add(v, u, 0, 0);
G[v].push_back(cnt++);
}
bool BFS() {
memset(vis, 0, sizeof(vis));
queue<int> q;
q.push(s);
vis[s] = 1;
d[s] = 0;
while (!q.empty()) {
int v = q.front(); q.pop();
for (int i = 0; i < G[v].size(); i++) {
Edge &te = e[G[v][i]];
if (!vis[te.v] && te.cap > te.flow) { //只考虑残量网络的弧
vis[te.v] = 1;
d[te.v] = d[v] + 1;
q.push(te.v);
}
}
}
return vis[t];
}
int dfs(int x, int a) {
if (x == t || a == 0) return a;
int flow = 0, f;
for (int &i = cur[x]; i < G[x].size(); i++) { //从上次考虑的弧
Edge &te = e[G[x][i]];
if (d[x] + 1 == d[te.v] && (f = dfs(te.v, min(a, te.cap - te.flow))) > 0) {
te.flow += f;
e[G[x][i]^1].flow -= f;
flow += f;
a -= f;
if (a == 0) break;
}
}
return flow;
}
int Dinic() {
int flow = 0;
while (BFS()) {
memset(cur, 0, sizeof(cur));
flow += dfs(s, INF);
}
return flow;
}
静态Trie
const int maxnNode = 1e6 + 5;
int ch[maxnNode][26];
int val[maxnNode];
struct Trie {
int sz;
Trie() {
sz = 1; memset(val, 0, sizeof(val));
memset(ch[0], 0, sizeof(ch[0]));
}
int idx(char c) { return c - 'a'; }
void Insert(char *s, int v) {
int u = 0, n = strlen(s);
for (int i = 0; i < n; i++) {
int c = idx(s[i]);
if (!ch[u][c]) {
memset(ch[sz], 0, sizeof(ch[sz]));
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
val[u] += v;
}
//字符串的字符的附加信息
}
int Query(char *s) {
int u = 0, n = strlen(s);
for (int i = 0; i < n; i++) {
int c = idx(s[i]);
if (!ch[u][c]) return 0;
u = ch[u][c];
}
return val[u];
}
};
区间线段树
struct Edge {
int v, next;
} e[maxn<<1];
struct TREE{
int l, r; ll val, maxx, lazy;
void fun(ll tmp) { //如果是直接赋值就用 =
lazy += tmp;
val += (r - l + 1) * tmp;
maxx += tmp;
}
} tre[maxn*4];
void PushDown(int id) {
if (tre[id].lazy) {
tre[id<<1].fun(tre[id].lazy);
tre[id<<1|1].fun(tre[id].lazy);
tre[id].lazy = 0;
}
}
void PushUp(int id) {
tre[id].val = tre[id<<1].val + tre[id<<1|1].val;
tre[id].maxx = max(tre[id<<1].maxx, tre[id<<1|1].maxx);
}
void build(int id, int l, int r) {
tre[id].l = l, tre[id].r = r, tre[id].lazy = 0;
if (l == r) tre[id].val = tre[id].maxx = dis[idx[l]];
else {
int mid = (l+r) >> 1;
build(id<<1, l, mid);
build(id<<1|1, mid+1, r);
PushUp(id);
}
}
void update(int id, int st, int ed, int val) {
int l = tre[id].l, r = tre[id].r;
if (st <= l && ed >= r) tre[id].fun(val);
else {
PushDown(id);
int mid = (l+r) >> 1;
if (st <= mid) update(id<<1, st, ed, val);
if (ed > mid) update(id<<1|1, st, ed, val);
PushUp(id);
}
}
ll query(int id, int st, int ed) {
int l = tre[id].l, r = tre[id].r;
if (st <= l && ed >= r) return tre[id].maxx;
else {
PushDown(id);
int mid = (l+r) >> 1;
ll sum1 = -inf, sum2 = -inf;
if (st <= mid) sum1 = query(id<<1, st, ed);
if (ed > mid) sum2 = query(id<<1|1, st, ed);
PushUp(id);
return max(sum1, sum2);
}
}
01字典树(XOR最大值)
const int maxnNode = 32*maxn;
int ch[maxnNode][2];
ll val[maxnNode];
int num[maxnNode];
int sz;
//因为是32 所以记得开long long
void init() {
memset(ch[0], 0, sizeof(ch[0]));
sz = 1;
}
//对于一个从来没有出现过的数一定要先插入 这样才有编号
void Insert(ll a, int d) {
int u = 0;
for (int i = 32; i >= 0; i--) {
int c = ((a>>i) & 1);
if (!ch[u][c]) {
memset(ch[sz], 0, sizeof(ch[0]));
num[sz] = 0;
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
num[u] += d; //记录当前数出现几次
}
val[u] = a; //记录当前数的值
}
void update(ll a, int d) {
int u = 0;
for (int i = 32; i >= 0; i--) {
int c = ((a>>i) & 1);
u = ch[u][c];
num[u] += d;
}
}
ll query(ll a) {
int u = 0;
for (int i = 32; i >= 0; i--) {
int c = ((a>>i) & 1);
if (ch[u][c^1] && num[ch[u][c^1]]) u = ch[u][c^1];
else u = ch[u][c];
}
return a ^ val[u];
}
int a[maxn];ll sum[maxn];
void solve() {
int n;
while (cin >> n) {
init();
Insert(0, 1); //记得插入0
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
}
int tot = 0; sum[0] = 0;
for (int i = 1; i <= n; i++) {
sum[i] = max(sum[i-1], query(a[i]));
tot ^= a[i];
Insert(tot, 1);
}
init(); Insert(0, 1);
tot = 0;
ll ans = 0;
for (int i = n; i >= 2; i--) {
ll tmp = query(a[i]);
ans = max(ans, tmp + sum[i-1]);
tot ^= a[i];
Insert(tot, 1);
}
cout << ans << endl;
}
}
树链剖分(注意树链剖分也是满足dfs序列的)
int n, head[maxn], cnt, tim; //tim是节点的编号
int fa[maxn], top[maxn], dep[maxn]; //top是链的最高的节点
int siz[maxn], out[maxn], son[maxn], id[maxn], a[maxn], pos[maxn]; //id是节点的编号,pos是编号的节点
struct Edge {
int v, next;
} e[maxn<<1];
struct TREE{
int l, r; int val, maxx, lazy;
void fun(int tmp) { //如果是直接赋值就用 =
lazy = tmp;
val = (r - l + 1) * tmp;
maxx = tmp;
}
} tre[maxn*4];
struct An {
int maxx, sum;
};
void PushDown(int id) {
if (tre[id].lazy) {
tre[id<<1].fun(tre[id].lazy);
tre[id<<1|1].fun(tre[id].lazy);
tre[id].lazy = 0;
}
}
void PushUp(int id) {
tre[id].val = tre[id<<1].val + tre[id<<1|1].val;
tre[id].maxx = max(tre[id<<1].maxx, tre[id<<1|1].maxx);
}
void build(int id, int l, int r) {
tre[id].l = l, tre[id].r = r, tre[id].lazy = 0;
if (l == r) tre[id].val = tre[id].maxx = a[pos[l]];
else {
int mid = (l+r) >> 1;
build(id<<1, l, mid);
build(id<<1|1, mid+1, r);
PushUp(id);
}
}
void update(int id, int st, int ed, int val) {
int l = tre[id].l, r = tre[id].r;
if (st <= l && ed >= r) tre[id].fun(val);
else {
PushDown(id);
int mid = (l+r) >> 1;
if (st <= mid) update(id<<1, st, ed, val);
if (ed > mid) update(id<<1|1, st, ed, val);
PushUp(id);
}
}
int query_sum(int id, int st, int ed) { //f -> sum
int l = tre[id].l, r = tre[id].r;
if (st <= l && ed >= r) {
return tre[id].val;
}
else {
PushDown(id);
int mid = (l+r) >> 1;
int tmp1 = 0, tmp2 = 0;
if (st <= mid) tmp1 = query_sum(id<<1, st, ed);
if (ed > mid) tmp2 = query_sum(id<<1|1, st, ed);
PushUp(id);
return tmp1 + tmp2;
}
}
int query_max(int id, int st, int ed) { //f -> sum
int l = tre[id].l, r = tre[id].r;
if (st <= l && ed >= r) {
return tre[id].maxx;
}
else {
PushDown(id);
int mid = (l+r) >> 1;
int tmp1 = -INF, tmp2 = -INF;
if (st <= mid) tmp1 = query_max(id<<1, st, ed);
if (ed > mid) tmp2 = query_max(id<<1|1, st, ed);
PushUp(id);
return max(tmp1, tmp2);
}
}
void init() {
tim = cnt = 0;
memset(head, -1, sizeof(head));
memset(son, -1, sizeof(son));
}
void add(int u, int v) {
e[cnt].v = v;
e[cnt].next = head[u];
head[u] = cnt++;
}
void dfs1(int u, int deep) {
dep[u] = deep; siz[u] = 1;
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].v;
if (v == fa[u]) continue;
fa[v] = u;
dfs1(v, deep + 1);
siz[u] += siz[v];
if (son[u] == -1 || siz[son[u]] < siz[v])
son[u] = v;
}
}
void dfs2(int u, int tp) {
top[u] = tp;
id[u] = ++tim;
pos[tim] = u;
if (son[u] == -1) {
out[u] = tim;//叶子节点必须要在这里写
return;
}
dfs2(son[u], tp);
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].v;
if (v != son[u] && v != fa[u])
dfs2(v, v);
}
out[u] = tim;
}
int Find(int u, int v, int f) { // f != 0 代表求和, f == 0代表求最值
int tp1 = top[u], tp2 = top[v];
int tmp = -INF;
if (f) tmp = 0;
while (tp1 != tp2) {
if (dep[tp1] < dep[tp2]) { //从u开始跳 保证u最低
swap(tp1, tp2); swap(u, v);
}
if (f) tmp += query_sum(1, id[tp1], id[u]);
else tmp = max(tmp, query_max(1, id[tp1], id[u]));
u = fa[tp1]; tp1 = top[u];
}
if (dep[u] < dep[v]) swap(u, v);
if (f) tmp += query_sum(1, id[v], id[u]);
else tmp = max(tmp, query_max(1, id[v], id[u])); //同一条链上的越上面的节点编号越小
return tmp;
}
void solve() {
while (cin >> n) {
init();
for (int i = 1; i < n; i++) {
int u, v; scanf("%d%d", &u, &v);
add(u, v); add(v, u);
}
for (int i = 1; i <= n; i++)
scanf("%d", a + i);
dfs1(1, 0);
dfs2(1, 1);
build(1, 1, n);
int q; cin >> q;
while (q--) {
char op[10]; int u, v;
scanf("%s%d%d", op, &u, &v);
if (op[0] == 'Q') {
if (op[1] == 'M') {
printf("%d\n", Find(u, v, 0));
}
else {
printf("%d\n", Find(u, v, 1));
}
}
else {
update(1, id[u], id[u], v);
}
}
}
}
莫队:
离线的思想,但是对于一些刁钻的数据,离线的复杂度不是很理想,所以就用到分块的思想,将每
一块的左端点的间隔设置到了最多sqrt(n)的间距,这样就能使复杂度降低。感觉写端点移动的时候比较烦。
排序地方的代码:
bool cmp1(const Query &a, const Query &b) {
if (pos[a.l] == pos[b.l])
return a.r < b.r;
return pos[a.l] < pos[b.l]; //以分块为第一关键字
}
bool cmp2(const Query &a, const Query &b) {
return a.id < b.id; //按顺序输出
}
分块的:
int limit = sqrt((db)n + 0.5); //每一块的大小
for (int i = 1; i <= n; i++)
pos[i] = (i-1) / limit + 1;
//分块,我也不知道为啥要(i-1) / limit + 1,我觉得直接 i / limit就可以了呀
for (int i = 1; i <= m; i++) {
scanf("%lld%lld", &q[i].l, &q[i].r);
q[i].id = i;
}
移动的:
while (l < q[i].l) {
sum -= (f[a[l]] << 1) - 1;
f[a[l]]--;
l++;
}
while (l > q[i].l) {
l--;
sum += (f[a[l]] << 1) + 1;
f[a[l]]++;
}
while (r < q[i].r) {
r++;
sum += (f[a[r]] << 1) + 1;
f[a[r]]++;
}
while (r > q[i].r) {
sum -= (f[a[r]] << 1) - 1;
f[a[r]]--;
r--;
}
hash:
使用 hash[i]=(hash[i-1]*p+idx(s[i]))%mod 求得前缀为i的hash值,利用 hash[l..r]=(hash[r]-hash[l-1]*(p^(r-1+1)))%mod 求得s[l,r]的hash值.(注意l=0的问题,以及hash[l..r] < 0时要 +mod)
代码
ll _hash(char *s) {
ll hhas = 0;
int len = strlen(s);
for (int i = 1; i <= len; i++)
hhas = (hhas * hp + s[i-1]) % mod;
return hhas;
}
ll getl_r(int l, int r) {
return (hashh[r] - hashh[l-1] * _p % mod + mod) % mod;
}
点双联通的新版子 就用这个(POJ那道题的)
int cas = 1;
int n, m;
int head[maxn], cnt, dfn[maxn], low[maxn];
int bcc_cnt, tim, bccno[maxn];
bool iscut[maxn];
vector<int> bcc[maxn];
stack<int> S;
struct Edge {
int u, v, w, next;
} e[maxn*maxn];
void init() {
bcc_cnt = cnt = 0;
memset(head, -1, sizeof(head));
memset(iscut, 0, sizeof(iscut));
memset(dfn, 0, sizeof(dfn));
}
void add(int u, int v, int c) {
e[cnt].u = u;
e[cnt].v = v;
e[cnt].w = c;
e[cnt].next = head[u];
head[u] = cnt++;
}
void tarjan(int u, int fa) {
dfn[u] = low[u] = ++tim;
int child = 0;
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].v;
if (!dfn[v]) {
S.push(i);
child++;
tarjan(v, u);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u]) {
iscut[u] = true;
bcc[++bcc_cnt].clear();
while (1) {
int tmp = S.top(); S.pop();
bccno[tmp] = bcc_cnt;
bcc[bcc_cnt].push_back(tmp);
bccno[tmp^1] = bcc_cnt;
bcc[bcc_cnt].push_back(tmp^1);
if (e[tmp].u == u && e[tmp].v == v) break;
}
}
}
else if (dfn[v] < dfn[u] && v != fa) {
S.push(i);
low[u] = min(low[u], dfn[v]);
}
}
if (fa == -1 && child == 1) iscut[u] = false;
}
void solve() {
int cas = 1;
while (1) {
int n = 0;
int u, v; scanf("%d", &u);
if (!u) break;
init();
scanf("%d", &v);
n = max(n, max(u, v));
add(u, v, 1); add(v, u, 1);
while (1) {
scanf("%d", &u);
if (!u) break;
scanf("%d", &v);
add(u, v, 1); add(v, u, 1);
n = max(n, max(u, v));
}
printf("Network #%d\n", cas++);
for (int i = 1; i <= n; i++)
if (!dfn[i]) tarjan(i, -1);
int flag = 0;
for (int i = 1; i <= n; i++) {
int vis[maxn] = {0}, num = 0;
if (iscut[i]) {
for (int j = head[i]; ~j; j = e[j].next) {
if (!vis[bccno[j]]) {
vis[bccno[j]] = 1;
num++;
}
}
flag |= 1;
printf(" SPF node %d leaves %d subnets\n", i, num);
}
}
if (!flag) cout << " No SPF nodes\n";
cout << endl;
}
}


浙公网安备 33010602011771号