省选模板
基础篇
1.高精度
const int maxD = 1010;
struct Huge
{
int len, ele[maxD];
Huge(): len(1) {memset(ele, 0, sizeof ele);}
Huge &operator=(const Huge &b)
{
memcpy(ele, b.ele, sizeof ele);
len = b.len; return *this;
}
int &operator[](int Ind) {return ele[Ind];}
};
int cmp(const Huge &a, const Huge &b)
{
int tmp = (a.len>b.len ? a.len : b.len)-1;
while(tmp>-1&& a[tmp]==b[tmp])--tmp;
return tmp>-1 ? a[tmp]-b[tmp] : 0;
}
void plus(const Huge &a, const Huge &b, Huge &c)
{
int len=a.len>b.len? a.len: b.len; c=Huge();
for (int i = 0; i < len; ++i)
{
c[i] += a[i] + b[i];
if (c[i] >= 10) c[i] -= 10, ++c[i + 1];
}
if (c[len]) ++len; c.len = len;
}
void subtract(const Huge&a,const Huge&b,Huge&c)
{
int len=a.len>b.len? a.len: b.len; c=Huge();
for (int i = 0; i < len; ++i)
{
c[i] += a[i] - b[i];
if (c[i] < 0) c[i] += 10, --c[i + 1];
}
while (len>1 && !c[len-1]) --len;
c.len = len;
}
void multiply10(Huge &a)
{
for (int i = a.len; i; --i) a[i] = a[i - 1];
a[0] = 0, ++a.len;
while (a.len>1 && !a[a.len-1]) --a.len;
}
void multiply(const Huge &a, int b, Huge &c)
{
int len = a.len; c = Huge();
for (int i = 0; i < len; ++i)
{
c[i] += a[i] * b;
c[i + 1] += c[i] / 10;
c[i] %= 10;
}
for (++len; c[len - 1] >= 10; ++len)
c[len] += c[len-1] / 10, c[len-1] %= 10;
while (len > 1 && !c[len - 1]) --len;
c.len = len;
}
void multiply(const Huge &a, const Huge &b, Huge &c)
{
c = Huge();
for (int i = 0; i < a.len; ++i)
for (int j = 0; j < b.len; ++j)
{
c[i + j] += a[i] * b[j];
c[i + j + 1] += c[i + j] / 10;
c[i + j] %= 10;
}
int len = a.len + b.len + 1;
while (len > 1 && !c[len - 1]) --len;
c.len = len;
}
void divide(const Huge&a, int b, Huge&c, int&d)
{
int len = a.len; c = Huge(); d = 0;
for (int i = len - 1; i > -1; --i)
d = d * 10 + a[i],
c[i] = d / b, d %= b;
while (len > 1 && !c[len - 1]) --len;
c.len = len;
}
void divide(const Huge &a, const Huge &b,
Huge &c, Huge &d)
{
Huge e; c = d = Huge(); int len=a.len;
for (int i = len - 1; i > -1; --i)
{
multiply10(d); d[0] = a[i];
while(cmp(d, b) > -1)
{subtract(d, b, e); d = e; ++c[i];}
}
while (len > 1 && !c[len - 1]) --len;
c.len = len;
}
2.快速排序
template <typename _Tp>
void qsort(_Tp *data, int l, int r,
bool (*cmp)(_Tp &, _Tp &))
{
int i = l, j = r, x = data[(l + r) >> 1];
do
{
while (cmp(data[i], x)) ++i;
while (cmp(x, data[j])) --j;
if (i<=j) {swap(data[i++], data[j--]);}
} while (i < j);
if (l < j) qsort(data, l, j, cmp);
if (i < r) qsort(data, i ,r, cmp);
}
3.归并排序
void MergeSort(int L, int R)
{
int Mid = L + R + 1 >> 1;
if (L < Mid - 1) MergeSort(L, Mid);
if (Mid < R - 1) MergeSort(Mid, R);
for (int f1 = L, f2 = Mid, f = 0;
f1 < Mid || f2 < R;)
if (!(f2 < R) || f1 < Mid && a[f1] < a[f2])
res[f++] = a[f1++];
else res[f++] = a[f2++];
for (int i=L; i<R; ++i) a[i] = res[i - L];
}
4.堆操作
inline void adjust_down(int i)
{
while ((i <<= 1) <= top)
{
if (i < top && cmp(hp[i], hp[i+1])) ++i;
if(cmp(hp[i >> 1], hp[i]))
swap(hp[i >> 1], hp[i]);
else break;
}
}
inline void adjust_up(int i)
{
for (; i >> 1; i >>= 1)
if(cmp(hp[i>>1],hp[i]))swap(hp[i>>1],hp[i]);
else break;
}
5.素数表生成器
inline void mkprime()
{
top = 0;
for (int i = 2; i <= n; ++i)
{
if (!tag[i]) p[top++] = i;
for (int j=0; j<top && p[j]*i <= n; ++j)
{
tag[i * p[j]] = 1;
if (i % p[j] == 0) break;
}
}
}
6.快速幂+矩阵乘法(迷路)
typedef int Matrix[maxN][maxN];
int tmp[maxR][maxR], n, T; Matrix mp = {0};
void readdata()
{
scanf("%d%d", &n, &T);
for (int i = 0; i < n; ++i)
{
getchar();
for (int j = 0; j < n; ++j)
tmp[i][j] = getchar() - '0';
}
for (int i = 0; i < n; ++i)
for (int k = 1; k < maxT; ++k)
mp[i * maxT + k - 1][i * maxT + k] = 1;
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
if (tmp[i][j])
//有边才能连边,否则无意义且出错。
mp[i*maxT + tmp[i][j] - 1][j*maxT]=1;
n *= maxT;
}
void Mul(Matrix &a, Matrix b)
{
Matrix ans = {0};
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
for (int k = 0; k < n; ++k)
(ans[i][j] += a[i][k] * b[k][j]) %= MOD;
//这里应注意容易出错。
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
a[i][j] = ans[i][j];
}
void Power(Matrix &mp, int T)
{
Matrix ans, tmp;
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
{ans[i][j] = 0; tmp[i][j] = mp[i][j] % MOD;}
for (int i = 0; i < n; ++i) ans[i][i] = 1;
for (; T; T >>= 1)
{if (T & 1) Mul(ans, tmp); Mul(tmp, tmp);}
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
mp[i][j] = ans[i][j] % MOD;
}
void work()
{Power(mp, T); printf("%d\n", mp[0][n-maxT]);}
7.并查集
int f[maxN], n, m, p;
int Find(int x)
{return (f[x]==x)?x:f[x]=Find(f[x]);}
void Merge(int x, int y) {f[Find(y)] = Find(x);}
void Judge(int x, int y)
{cout<< (Find(x)==Find(y) ? “Yes\n” : “No\n”);}
void work()
{
cin >> n >> m;
for (int i = 1; i < n + 1; ++i) f[i] = i;
for (int i = 0; i < m; ++i)
{int x, y; cin >> x >> y; Merge(x, y);}
cin >> p;
for (int i = 0; i < p; ++i)
{int x, y; cin >> x >> y; Judge(x, y);}
}
8.最大子立方(吃西瓜)
int s[60][60][60], x[60][60][60], s1[60][60];
int n, m, h, ans = -0x7fffff00;
int main()
{
scanf("%ld%ld%ld", &n, &m, &h);
for (int i = 1; i < n + 1; ++i)
for (int j = 1; j < m + 1; ++j)
for (int k = 1; k < h + 1; ++k)
scanf("%ld", &x[i][j][k]);
for (int i = 1; i < n + 1; ++i)
for (int j = 1; j < m + 1; ++j)
for (int k = 1; k < h + 1; ++k)
s[i][j][k] = s[i - 1][j][k]+x[i][j][k];
for (int i1 = 0; i1 < n; ++i1)
for (int i2 = i1 + 1; i2 < n + 1; ++i2)
{
memset(s1, 0, sizeof(s1));
for (int j = 1; j < m + 1; ++j)
for (int k = 1; k < h + 1; ++k)
s1[j][k] = s1[j - 1][k] +
s[i2][j][k] - s[i1][j][k];
for (int j1 = 0; j1 < m; ++j1)
for (int j2 = j1 + 1; j2 < m + 1; ++j2)
{
int tmp = s1[j2][1] - s1[j1][1];
for (int k = 2; k < h + 1; ++k)
{
tmp = std::max(tmp, 0);
tmp += s1[j2][k] - s1[j1][k];
ans = std::max(tmp, ans);
}
}
}
printf("%ld", ans); return 0;
}
9.悬线法求极大子矩形(糖果盒)
int a[maxN][maxN], H[maxN], sum[maxN][maxN];
int maxL[2][maxN], maxR[2][maxN];
int L[maxN], R[maxN], n, m, ans;
int main()
{
n = getint(); m = getint();
for (int i = 1; i < n + 1; ++i)
for (int j = 1; j < m + 1; ++j)
sum[i][j] = sum[i][j - 1]
+ (a[i][j] = getint());
for (int i = 1; i < n + 1; ++i)
for (int j = 1; j < m + 1; ++j)
sum[i][j] += sum[i - 1][j];
for (int j=1; j<=m; ++j) maxR[0][j] = m;
R[m + 1] = m;
//这里要赋初值,否则出错。
for (int i = 1; i < n + 1; ++i)
{
int pst = (i & 1) ^ 1, ths = i & 1;
for (int j = 1; j < m + 1; ++j)
if (!a[i][j])
H[j]=i, L[j]=j, maxL[ths][j]=0;
//如果该行该列的点为障碍,
//那么下一行的maxL值跟这一行无关,设为0。
else
{
L[j] = L[j - 1];
maxL[ths][j]=max(L[j],maxL[pst][j]);
}
for (int j = m; j; --j)
if (!a[i][j])
R[j] = j - 1, maxR[ths][j] = m;
//如果该行该列的点为障碍,
//那么下一行的maxR值跟这一行无关,设为m。
else
{
R[j] = R[j + 1];
maxR[ths][j]=min(R[j],maxR[pst][j]);
int _L=maxL[ths][j],_R=maxR[ths][j],
_U = H[j];
ans=max(ans,sum[i][_R]+sum[_U][_L]
-sum[i][_L]-sum[_U][_R]);
}
}
printf("%d\n", ans); return 0;
}
10.多重背包(逃亡的准备)
int f[maxV], a[maxN], w[maxN], v[maxN], n, m;
void readdata()
{
scanf("%d%d", &n, &m);
for (int i = 1; i < n + 1; ++i)
scanf("%d%d%d", a + i, w + i, v + i);
}
void work()
{
for (int i = 1; i < n + 1; ++i)
{
if (a[i] * w[i] >= m)
for (int j = w[i]; j < m + 1; ++j)
f[j] = max(f[j], f[j-w[i]] + v[i]);
else
{
for (int k = 1; k < a[i]; k <<= 1)
{
for (int j = m; j >= k*w[i]; --j)
f[j] = max(f[j], f[j - k*w[i]]
+ k * v[i]);
a[i] -= k;
}
for (int j = m; j >= a[i]*w[i]; --j)
f[j] = max(f[j], f[j-a[i]*w[i]]
+ a[i] * v[i]);
}
}
printf("%d", f[m]);
}
11.读入整数的优化:
无符号:
inline int getint()
{
int res = 0; char tmp;
while (!(isdigit(tmp = getchar())));
do res = (res<<3) + (res<<1) + tmp - '0';
while ((isdigit(tmp = getchar())));
return res;
}
有符号:
inline int getint()
{
int res = 0; char tmp; bool sgn = 1;
do tmp = getchar();
while (!isdigit(tmp) && tmp != '-');
if (tmp=='-') {sgn = 0; tmp = getchar();}
do res = (res<<3) + (res<<1) + tmp - '0';
while (isdigit(tmp = getchar()));
return sgn ? res : -res;
}
省选篇
1.线段树:(Mayor's Poster)
struct SegTree {int L, R, lc, rc, color;};
struct Seg {int pos, Ind, sym;} tree[maxN << 1];
Seg seg[maxN << 1]; bitset <maxN> marked;
int L[maxN], R[maxN], n, N, tot, cnt;
int cmp(const void *a, const void *b)
{return ((Seg *)a)->pos - ((Seg *)b)->pos;}
void Build(int L, int R)
{
int Now = ++tot;
tree[Now].L = L; tree[Now].R = R;
tree[Now].lc=tree[Now].rc=tree[Now].color=0;
//所有标记清零。
if (L + 1 < R)
{
int Mid = (L + R) >> 1;
tree[Now].lc = tot + 1; Build(L, Mid);
tree[Now].rc = tot + 1; Build(Mid, R);
} //递归建树。
}
void insert(int Now, int i)
{
if (L[i]>tree[Now].R || R[i]<tree[Now].L)
return;
//若当前被插入的线段与这个区间无交集,则不需要遍历。
if (L[i]<=tree[Now].L && R[i]>=tree[Now].R)
{tree[Now].color = i; return;}
//若该区间被完全复盖,直接染色并退出。
if (tree[Now].color > -1)
{
tree[tree[Now].lc].color =
tree[tree[Now].rc].color =
tree[Now].color;
tree[Now].color = -1;
} //若开始时该区间为单色或无色,
//则标记向下传,并把该区间标记为多色。
int Mid = (tree[Now].L + tree[Now].R) >> 1;
if (L[i] < Mid) insert(tree[Now].lc, i);
if (Mid < R[i]) insert(tree[Now].rc, i);
//依次对它的左右子区间进行统计。
}
void count(int Now)
{
if (tree[Now].color == 0) return;
//如果该区间没有颜色,那么其子区间不需要被遍历。
if (tree[Now].color > 0)
{
if (!marked.test(tree[Now].color))
{marked.set(tree[Now].color); ++cnt;}
return;
} //若该区间为单色,统计该颜色后
//就不需要对其子区间进行统计了。
if (tree[Now].lc) count(tree[Now].lc);
if (tree[Now].rc) count(tree[Now].rc);
//若该区间为多色,那么需要对各个子区间分别进行统计。
}
void work()
{
scanf("%d", &N);
for (; N; --N)
{
scanf("%d", &n); int x, y; tot = 0;
for (int i = 1; i < n + 1; ++i)
{
scanf("%d%d", &x, &y);
seg[++tot].pos = x;
seg[tot].sym = 1; seg[tot].Ind = i;
seg[++tot].pos = y + 1;
seg[tot].sym = -1; seg[tot].Ind = i;
}
qsort(seg + 1, n<<1, sizeof seg[0], cmp);
//按照座标从左到右排序,方便压缩。
int Last = 1;
if (seg[1].sym == 1) L[seg[1].Ind] = 1;
if (seg[1].sym == -1) R[seg[1].Ind] = 1;
for (int i = 2; i < (n << 1) + 1; ++i)
if (seg[i].pos == seg[i - 1].pos)
{
if(seg[i].sym==1) L[seg[i].Ind]=Last;
if(seg[i].sym==-1)R[seg[i].Ind]=Last;
}
else if (seg[i].pos > seg[i - 1].pos)
//压缩区间,“毁掉”不需要用掉的空间。
{
if (seg[i].sym == 1)
L[seg[i].Ind] = ++Last;
if (seg[i].sym == -1)
R[seg[i].Ind] = ++Last;
}
tot = 0; Build(1, Last); //建立线段树。
for (int i=1; i <= n; ++i) insert(1, i);
marked.reset(); cnt = 0; count(1);
printf("%d\n", cnt);
}
}
2.平衡二叉树(SBT)
class SBT
{
private:
int key[maxN << 2], sz[maxN << 2], T;
int lc[maxN << 2], rc[maxN << 2], tot;
void Zig(int &T)
{
int tmp = lc[T]; lc[T] = rc[tmp];
rc[tmp] = T; sz[tmp] = sz[T];
sz[T] = sz[lc[T]] + sz[rc[T]] + 1;
T = tmp;
}
void Zag(int &T)
{
int tmp = rc[T]; rc[T] = lc[tmp];
lc[tmp] = T; sz[tmp] = sz[T];
sz[T] = sz[lc[T]] + sz[rc[T]] + 1;
T = tmp;
}
void maintain(int &T, bool flag)
{
if (!T || !lc[T] && !rc[T]) return;
if (!flag)
{
if (sz[lc[lc[T]]] > sz[rc[T]]) Zig(T);
else if (sz[rc[lc[T]]] > sz[rc[T]])
{Zag(lc[T]); Zig(T);}
else return;
}
else
{
if (sz[rc[rc[T]]] > sz[lc[T]]) Zag(T);
else if (sz[lc[rc[T]]] > sz[lc[T]])
{Zig(rc[T]); Zag(T);}
else return;
}
maintain(lc[T], 0); maintain(rc[T], 1);
maintain(T, 0); maintain(T, 1);
}
void Ins(int &T, int v)
{
if (!T) {sz[T = ++tot] = 1; key[T] = v;}
++sz[T];
if (v < key[T]) Ins(lc[T], v);
else Ins(rc[T], v);
maintain(T, v >= key[T]);
}
int Del(int &T, int v)
{
--sz[T];
if (v == key[T]
|| v < key[T] && !lc[T]
|| v > key[T] && !rc[T])
{
int tmp = key[T];
if (!lc[T] || !rc[T]) T = lc[T]+rc[T];
else key[T] = Del(lc[T], key[T]);
return tmp;
}
if (v < key[T]) return Del(lc[T], v);
else return Del(rc[T], v);
}
int pred(int &T, int v, int ans)
{
if (!T) return ans;
if (key[T]<v && key[T]>ans) ans=key[T];
if (v<key[T]) return pred(lc[T], v, ans);
else return pred(rc[T], v, ans);
}
int succ(int &T, int v, int ans)
{
if (!T) return ans;
if (!(key[T]<v)&&key[T]<ans) ans=key[T];
if (v<key[T]) return succ(lc[T], v, ans);
else return succ(rc[T], v, ans);
}
int min(int T)
{return lc[T] ? min(lc[T]) : key[T];}
int max(int T)
{return rc[T] ? max(rc[T]) : key[T];}
public:
SBT(): tot(0), T(0)
{
memset(key, 0, sizeof key);
memset(lc, 0, sizeof lc);
memset(rc, 0, sizeof rc);
memset(sz, 0, sizeof sz);
}
void Ins(int v) {Ins(T, v); return;}
void Del(int v) {Del(T, v); return;}
int pred(int v) {return pred(T, v, ~INF);}
int succ(int v) {return succ(T, v, INF);}
int min() {return min(T);}
int max() {return max(T);}
};
3.最大流(SAP算法)(ditch)
int flow[maxN][maxN], d[maxN], cnt[maxN], n, m;
void readdata()
{
scanf("%d%d", &m, &n); int u, v, e;
for (int i = 0; i < m; ++i)
{
scanf("%d%d%d", &u, &v, &e);
flow[u][v] += e;//从u到v可能有多条流量限制。
}
}
int Sap(int x, int Lim)
{
if (x == n) return Lim;
//若已经到了n点,则说明已经找到了增广路,返回流量。
int tmp = 0;
for (int i = 1; i < n + 1; ++i)
if (flow[x][i] > 0 && d[x] == d[i] + 1)
//从x节点的上一层寻找可增广路,若找到则更新当前流量。
{
int k=Sap(i, min(Lim-tmp, flow[x][i]));
//递归找增广路。
flow[x][i] -= k;
flow[i][x] += k; //修改残量网络。
if ((tmp += k) == Lim) return tmp;
//若当前流量已经达到最大限度,则返回当前流量。
}
if (d[1] >= n) return tmp; //无法增广则返回。
if ((--cnt[d[x]]) <= 0) d[1] = n;
//出现断层则不再存在增广路。
++cnt[++d[x]];
//当前节点在第d[x]层已不存在增广路,则把x节点往上提。
return tmp;
//最后一定要返回当前的以求得的流量!!!!
}
//此模块表示从x节点到汇点的流量上限为Lim的增广路。
void work()
{
int ans = 0; cnt[0] = n;
while (d[1] < n) ans += Sap(1, MAX);
//只要还可以增广就继续增广。
printf("%d", ans);
}
4.最大匹配(匈牙利算法)(奶牛分配)
struct Edge {int dest;Edge *next;} *edge[maxN];
int Link[maxN], n, m, t, c, ans;
std::bitset <maxN> marked;
inline void insert(int u, int v)
{
Edge *p = new Edge; p -> dest = v;
p -> next = edge[u]; edge[u] = p;
}
void readdata()
{
scanf("%d%d", &n, &m);
for (int i = 1; i < n + 1; ++i)
for (scanf("%d", &t);; t; --t)
{scanf("%d", &c); insert(i, c);}
}
bool Find(int u)
{
for (Edge *p = edge[u]; p; p = p -> next)
{
int v = p -> dest;
if (marked[v]) continue;else marked[v]=1;
if (!Link[v] || Find(Link[v]))
{Link[v] = u; return true;}
}
return false;
}
void work()
{
for (int i = 1; i < n + 1; ++i)
{marked.reset(); if (Find(i)) ++ans;}
printf("%d", ans);
}
5.最佳匹配(KM算法)(丘比特的烦恼)
struct vec
{
int x, y; vec() {}
vec(int x, int y): x(x), y(y) {}
vec operator-(const vec &b) const
{return vec(x - b.x, y - b.y);}
int operator+(const vec &b) const
{return x * b.x + y * b.y;}
int operator*(const vec &b) const
{return x * b.y - y * b.x;}
int norm() const {return x * x + y * y;}
bool btwn(const vec &A, const vec &B) const
{
vec OA = vec(A.x - x, A.y - y),
OB = vec(B.x - x, B.y - y);
return !(OA * OB) && OA + OB < 0;
}
};
map <string, int> _boy, _girl;
vec boy[maxN], girl[maxN];
bool b[maxN], g[maxN];
int lx[maxN], ly[maxN], Link[maxN];
int mp[maxN][maxN], n, K;
void readdata()
{
cin >> K >> n;
for (int i = 0; i < n; ++i)
{
int x, y; string str;
cin >> x >> y >> str;
boy[i] = vec(x, y);
transform(str.begin(), str.end(),
str.begin(), ::tolower);
//将一个string类型转换为小写。
_boy[str] = i;
}
for (int i = 0; i < n; ++i)
{
int x, y; string str;
cin >> x >> y >> str;
girl[i] = vec(x, y);
transform(str.begin(), str.end(),
str.begin(), ::tolower);
_girl[str] = i;
Link[i] = -1;
}
int w; string s1, s2;
while (cin >> s1 >> s2 >> w)
{
transform(s1.begin(), s1.end(),
s1.begin(), ::tolower);
transform(s2.begin(), s2.end(),
s2.begin(), ::tolower);
if (_boy.find(s1) == _boy.end())
swap(s1, s2);
mp[_boy[s1]][_girl[s2]] = w;
}
}
void modify()
{
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
{
if (!mp[i][j]) mp[i][j] = 1;
if ((boy[i] - girl[j]).norm() > K * K)
{mp[i][j] = MIN; continue;}
if (mp[i][j] > 0)
for (int i1 = 0; i1 < n; ++i1)
if (i1-i&&boy[i1].btwn(boy[i],girl[j]))
{mp[i][j] = MIN; break;}
if (mp[i][j] > 0)
for (int j1 = 0; j1 < n; ++j1)
if (j1-j&&girl[j1].btwn(boy[i],girl[j]))
{mp[i][j] = MIN; break;}
lx[i] = max(lx[i], mp[i][j]);
} //当不能连边时一定要把权值设为负无穷。
}
bool Find(int i)
{
b[i] = 1;
for (int j = 0; j < n; ++j)
if (lx[i] + ly[j] == mp[i][j] && !g[j])
{
g[j] = 1;
if (Link[j] == -1 || Find(Link[j]))
{Link[j] = i; return 1;}
}
return 0;
}
void work()
{
for (int k = 0; k < n; ++k)
while (1)
{
memset(b, 0, sizeof b);
memset(g, 0, sizeof g);
if (Find(k)) break; int Min = MAX;
for (int i = 0; i < n; ++i) if (b[i])
for (int j = 0; j < n; ++j) if (!g[j])
Min = min(Min,lx[i]+ly[j]-mp[i][j]);
for (int i = 0; i < n; ++i)
{
if (b[i]) lx[i] -= Min;
if (g[i]) ly[i] += Min;
}
}
int ans = 0;
for (int j=0; j<n; ++j) ans+=mp[Link[j]][j];
printf("%d\n", ans);
}
6.最小费用流(运输问题)
struct Edge
{int u, v, f_min, f_max, d; Edge *next, *back;};
Edge *edge[maxN], *pre[maxN];
bool marked[maxN];
int dist[maxN], q[SIZE + 1], n, m, S, T, f, r;
inline void Ins(int u, int v, int f, int d)
{
Edge *p = new Edge;
p -> u = u; p -> v = v; p -> d = d;
p -> f_min = p -> f_max = f;
p -> next = edge[u]; edge[u] = p;
p = new Edge;
p -> u = v; p -> v = u; p -> d = -d;
p -> f_min = p -> f_max = 0;
p -> next = edge[v]; edge[v] = p;
edge[u] -> back = edge[v];
edge[v] -> back = edge[u];
}
inline int min(int a,int b) {return a<b ? a:b;}
inline int max(int a,int b) {return a>b ? a:b;}
inline bool Spfa(int (*opt)(int, int))
{
memset(dist,opt(0,1)?0xc0:0x3f,sizeof dist);
memset(pre, 0, sizeof pre);
memset(marked, 0, sizeof marked);
dist[S] = 0; f=r=0; marked[q[r++] = S] = 1;
while (f - r)
{
int u = q[f++]; f &= SIZE; marked[u] = 0;
for (Edge *p = edge[u]; p; p = p -> next)
if (opt(0, 1) ? p -> f_max : p -> f_min)
{
int v= p -> v,
tmp = opt(dist[v], dist[u] + p -> d);
if (tmp - dist[v])
{
dist[v] = tmp; pre[v] = p;
if (!marked[v])
{marked[q[r++] = v] = 1; r&=SIZE;}
}
}
}
return (bool)pre[T];
}
int main()
{
n = getint(); m = getint();
S = n + m + 1; T = n + m + 2;
for (int i = 1; i < n + 1; ++i)
Ins(S, i, getint(), 0);
for (int j = 1; j < m + 1; ++j)
Ins(j + n, T, getint(), 0);
for (int i = 1; i < n + 1; ++i)
for (int j = 1; j < m + 1; ++j)
Ins(i, j + n, MAX, getint());
int Min = 0, Max = 0;
while (Spfa(min))
{
int Max_flow = MAX;
for (Edge *p = pre[T]; p; p = pre[p->u])
Max_flow = min(Max_flow, p -> f_min);
for (Edge *p = pre[T]; p; p = pre[p->u])
{
p -> f_min -= Max_flow;
p -> back -> f_min += Max_flow;
}
Min += Max_flow * dist[T];
}
while (Spfa(max))
{
int Max_flow = MAX;
for (Edge *p = pre[T]; p; p = pre[p->u])
Max_flow = min(Max_flow, p -> f_max);
for (Edge *p = pre[T]; p; p = pre[p->u])
{
p -> f_max -= Max_flow;
p -> back -> f_max += Max_flow;
}
Max += Max_flow * dist[T];
}
printf("%d\n%d\n", Min, Max); return 0;
}
7.凸包(Graham算法)(Wall)
typedef complex <double> Point;
Point p[maxN], res[maxN]; int n, R, top;
void readdata()
{
scanf("%d%d", &n, &R);
for (int i = 0; i < n; ++i)
{
double x, y; scanf("%lf%lf", &x, &y);
p[i] = Point(x, y);
}
}
template <typename _Tp>
_Tp outer_product(const complex <_Tp> a,
const complex <_Tp> b)
{return a.real()*b.imag()-a.imag()*b.real();}
int cmp(const void *a, const void *b)
{
Point A = *(Point *)a, B = *(Point *)b;
if (A.imag() < B.imag() - zero) return -1;
if (A.imag() > B.imag() + zero) return 1;
if (A.real() < B.real() - zero) return -1;
if (A.real() > B.real() + zero) return 1;
return 0;
}
void work()
{
qsort(p, n, sizeof(p[0]), cmp);
res[0] = p[0]; res[1] = p[1]; top = 1;
for (int i = 2; i < n; ++i)
{
while (top&&outer_product(p[i]-res[top],
res[top]-res[top-1])>-zero)
--top;
res[++top] = p[i];
}
int tmp = top; res[++top] = p[n - 2];
for (int i = n - 3; i > -1; --i)
{
while (top > tmp &&
outer_product(p[i] - res[top],
res[top] - res[top - 1]) > -zero)
--top;
res[++top] = p[i];
}
double ans=Pi*(R<<1)+abs(res[0]-res[top-1]);
for (int i = 1; i < top; ++i)
ans += abs(res[i] - res[i - 1]);
printf("%d\n", (int)(ans + 0.5));
}
8.RMQ问题
void rmq()
{
for (int i = 1; i <= n; ++i) f[i][0] = a[i];
for (int q = 0; 1 << q < n; ++q)
for (int i = 1; i + (1 << q) < n + 2; ++i)
f[i][q+1] = max(f[i+(1<<q)][q],f[i][q]);
}
int query(int L, int R)
{
int q = 0; for (; 1<<q < R-L+2; ++q); --q;
return max(f[L][q], f[R - (1<<q) + 1][q]);
}
9.无向图找桥和格点(danger)
struct Edge {int dest; Edge *next;}*edge[maxN];
bool bridge[maxN][maxN], marked[maxN];
int low[maxN], D[maxN], n, m, deep;
void insert(int u, int v)
{
Edge *p = new Edge; p -> dest = v;
p -> next = edge[u]; edge[u] = p;
}
void readdata()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int u, v; cin >> u >> v;
insert(u, v); insert(v, u);
}
}
void DFS(int u, int x)
{
D[u] = low[u] = ++deep; marked[u] = true;
for (Edge *p = edge[u]; p; p = p -> next)
{
int v = p -> dest;
if (v != x && !marked[v]) DFS(v, u);
//若v不是u的父节点,且v未被遍历过,则遍历v。
if (v != x && marked[v])
{
low[u] = min(low[u], D[v]);
low[u] = min(low[u], low[v]);
//以上两行更新low[u]的值。
if (D[u] < low[v])
bridge[u][v] = bridge[v][u] = 1;
//标记(u, v)为桥。
}
}
} //对节点u进行遍历。(其中,x为u的父节点。)
void print()
{
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
if (bridge[i][j]) cout<<i<<" "<<j<<endl;
}
void work()
{
deep = 0; //DFS序号初始化为0。
DFS(1, 0);
//从1开始(任意一个节点开始都可以)遍历,无父节点。
print();
}
无向图找桥:
对图深度优先搜索,定义D[u]为u在搜索树(以下简称为树)中被遍历到的次序号。
定义low[u]为u或u的子树中能通过非父子边追溯到的最早的节点,即DFS序号最小的节点。
根据定义,则有:
Low(u)=min{D[u], D[v], low[v]}
对于D[v]:(u,v)为后向边(返祖边、回边) 。
(等价于 DFS(v)<DFS(u)且v不为u的父亲节点。)
对于low[v]:(u,v)为树枝边(父子边,即v为u的子节点)。
一个顶点u是割点,当且仅当满足(1)或(2)
(1) u为树根,且u有多于一个子树。
(2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,
即u为v在搜索树中的父亲),使得D[u] <= low[v]。
一条无向边(u,v)是桥,当且仅当(u,v)为树枝边,且满足D[u] < low[v]。
10.强连通分量(Popular Cow's)
struct Edge {int v;Edge *next;};
Edge *edge[maxN]; bool marked[maxN];
int stack[maxN], belong[maxN], out[maxN];
int DFN[maxN], Low[maxN], n, top, Bcnt, Index;
inline void insert(int u, int v)
{
Edge *p = new Edge; p -> v = v;
p -> next = edge[u]; edge[u] = p;
}
void tarjan(int u)
{
DFN[u] = Low[u] = ++Index;
marked[stack[++top] = u] = true;
for (Edge *p = edge[u]; p; p = p -> next)
{
int v = p -> v;
if (!DFN[v])
{
tarjan(v);
if (Low[v] < Low[u]) Low[u] = Low[v];
}
else if (marked[v] && DFN[v] < Low[u])
Low[u] = DFN[v];
}
if (DFN[u] == Low[u])
{
++Bcnt; int tmp = u;
do
{
tmp = stack[top--];
marked[tmp] = false;
belong[tmp] = Bcnt;
} while (tmp != u);
}
}
int work()
{
for (int i = 1; i < n + 1; ++i)
if (!DFN[i]) tarjan(i);
for (int i = 1; i < n + 1; ++i)
for (Edge *p = edge[i]; p; p = p -> next)
if (belong[i] != belong[p -> v])
++out[belong[i]];
int pos = 0;
for (int i = 1; i < Bcnt + 1; ++i)
if (!out[i]) if (pos) return 0; else pos=i;
int ans = 0;
for (int i = 1; i < n + 1; ++i)
if (belong[i] == pos) ++ans;
return ans;
}
int main()
{
n = getint(); int m = getint();
for (; m; --m)
{int u=getint(), v=getint(); insert(u, v);}
printf("%d\n", work()); return 0;
}
Tarjan算法:
找出所有的强连通分量,分别统计每个强连通分量的点的个数和出度,若出度为0的强连通分量数为1,则答案等于此强连通分量的点的个数,否则答案为零。
11.最近公共祖先(向RMQ转化)(Tree)
#pragma comment(linker, "/STACK:0x10000000")
#define RMQ_min(a, b) (D[a] < D[b] ? (a) : (b))
struct Edge {int v, d; Edge *next;};
Edge *edge[maxN];
int D[maxORD], ord[maxORD], f[maxORD][20];
int fir[maxN], dist[maxN], Ind;
void Dfs(int u, int Last, int Dep)
{
ord[++Ind] = u; fir[u] = Ind; D[Ind] = Dep;
for (Edge *p = edge[u]; p; p = p -> next)
if (p -> v - Last)
{
dist[p -> v] = dist[u] + p -> d;
Dfs(p -> v, u, Dep + 1);
ord[++Ind] = u; D[Ind] = Dep;
}
}
inline void RMQ_set()
{
for (int i = 1; i <= Ind; ++i) f[i][0] = i;
for (int q = 0; 1 << q < Ind; ++q)
for (int i = 1; i + (1 << q) < Ind + 1; ++i)
f[i][q+1]=RMQ_min(f[i][q],f[i+(1<<q)][q]);
}
inline int RMQ(int L, int R)
{
if(L==R) return L; if(L>R) std::swap(L,R);
int q = 0; for (; 1<<q < R-L+2; ++q); --q;
return RMQ_min(f[L][q], f[R-(1<<q)+1][q]);
}
inline void Ins(int u, int v, int d)
{
Edge *p = new Edge; p -> v = v;
p -> d = d; p -> next = edge[u];
edge[u] = p;
}
int main()
{
for (int n = getint(); --n;)
{
int u=getint(), v=getint(), d=getint();
Ins(u, v, d); Ins(v, u, d);
}
Dfs(0, -1, 0); RMQ_set();
for (int m = getint(); m; --m)
{
int u = getint(), v = getint();
printf("%d\n", dist[u] + dist[v] -
(dist[ord[RMQ(fir[u], fir[v])]]<<1));
}
return 0;
}
#undef RMQ_min
12.AC自动机(Keywords Search)
struct AC_auto
{
AC_auto *next[26], *Fail;
//next指针即为各个字母(共26个字母);当匹配不
//成功时,Fail指针用于跳转到Trie树中的另一节点。
int cnt;
AC_auto(): Fail(NULL), cnt(0)
{memset(next, 0, sizeof next);}
};
AC_auto *q[SIZE + 1]; //用队列辅助建立AC自动机。
char key[maxL], str[maxLEN];
int n, T, f, r;
inline void insert(AC_auto*&root,const char*str)
{
AC_auto *p = root;
for (int i = 0; str[i]; ++i)
{
int Index = str[i] - 'a';
if (!(p -> next[Index]))
p -> next[Index] = new AC_auto();
p = p -> next[Index];
}
++(p -> cnt); //用于记录到此为止的单词数目。
}
//将字符串str插入到Trie树中,这棵树从根到叶(也可能
//是到中间的某个字母)的每一条路径上的字母为一个单词。
inline void build(AC_auto *&root)
{
root -> Fail = NULL; //根节点的失败指针为空。
f = r = 0;
for (q[r++] = root; f < r;)
{
AC_auto *Now = q[f++], *p = NULL;
for (int i=0;i<26;++i) if (Now->next[i])
//若当前节点有这棵子树,则遍历。
{
if (Now == root)
Now -> next[i] -> Fail = root;
//根的各个直接子节点的失败指针为根。
else for (p=Now->Fail; p; p=p->Fail)
if (p -> next[i])
{
Now->next[i]->Fail = p->next[i];
break;
}
//找当前节点的其它失败指针中是否存在该字母,
//若有,则把当前节点的子树的失败指针指向之。
if (!p) Now->next[i]->Fail = root;
//若没找到,则失败指针为根。
q[r++]=Now->next[i];//将这棵子树入队。
}
}
}
inline int query(AC_auto *root, const char *str)
{
int cnt = 0;
AC_auto *p = root;
for (int i = 0; str[i]; ++i)
{
int Index = str[i] - 'a';
while (!(p -> next[Index]) && p != root)
p = p -> Fail;
//此路不通时,找它的失败指针。
p = p -> next[Index];
p = p ? p : root;
for (AC_auto *tmp = p; tmp != root &&
tmp -> cnt != -1; tmp = tmp->Fail)
{
cnt += tmp -> cnt;
tmp -> cnt = -1; //防止多次统计。
} //顺便统计其他失败指针中的单词个数。
}
return cnt;
}
int main()
{
scanf("%d", &T);
for (; T; --T)
{
scanf("%d", &n);
AC_auto *root = new AC_auto();
for (; n; --n)
{scanf("%s", key); insert(root, key);}
build(root); scanf("%s", str);
printf("%d\n", query(root, str));
}
return 0;
}
13.左偏树(Financial Fraud)
struct LeftList
{
int key, dist; LeftList *lc, *rc;
LeftList() {}
LeftList(int key):
key(key), dist(0), lc(NULL), rc(NULL) {}
};
LeftList *tr[maxN];
int L[maxN], R[maxN], sz[maxN], a[maxN];
bool cmp(const LeftList *a,const LeftList *b)
{return a -> key > b -> key;}
//默认为小根,这里重载比较为大根。
LeftList *Merge(LeftList *a, LeftList *b)
{
if (!a) return b; if (!b) return a;
//待合并的两棵子树中,若其中一棵为空,则直接返回另一棵树。
if (cmp(b, a)) std::swap(a, b);
//为了方便操作,让根值较大(大根树)
//或较小(小根树)的树作为合并后的根。
a -> rc = Merge(a -> rc, b);
if (!(a -> lc) || a -> rc &&
a -> rc -> dist > a -> lc -> dist)
std::swap(a -> lc, a -> rc);
//调整左右子树的位置使其满足左偏树的性质。
//(左子树的距离不小于右子树的距离即左偏的性质。)
if (!(a -> rc)) a -> dist = 0;
else a -> dist = a -> rc -> dist + 1;
//更新合并后的树的距离。
return a;
}
inline void Del(LeftList *&a)
//这里引用符号“&”不能省。
{a = Merge(a -> lc, a -> rc);}
int main()
{
int n;
while (n = getint())
{
int top = 0;
for (int i = 0; i < n; ++i)
{
tr[++top]= new
LeftList(a[i] = getint());
sz[top] = 1; L[top] = R[top] = i;
//用L,R指针记录当前左偏树所代表的区间。
while (top > 1 && tr[top - 1] -> key
> tr[top] -> key)
{
tr[top-1] = Merge(tr[top – 1],
tr[top]);
sz[top - 1] += sz[top];
R[top - 1] = R[top];
for (--top; sz[top]>(R[top]
-L[top]>>1)+1;--sz[top])
Del(tr[top]);
//使得合并后的左偏树的根节点(即最大值)不超过该区间的中
//位数,即让左偏树中总节点数不超过该区间中总结点数的一半
}
}
long long ans = 0;
for (int i = 1; i < top + 1; ++i)
for (int j = L[i]; j < R[i] + 1; ++j)
ans += abs(a[j] - tr[i] -> key);
printf("%lld\n", ans);
}
return 0;
}
#undef abs
14.树状数组(Stars)
#define lowbit(x) ((x) & (-(x)))
int a[maxX], lev[maxN], n;
inline void Ins(int x)
{for (int i=x; i<maxX; i+=lowbit(i)) ++a[i];}
inline int Sum(int x)
{
int ans = 0;
for (int i=x; i; i-=lowbit(i)) ans += a[i];
return ans;
}
int main()
{
n = getint();
for (int i = 0; i < n; ++i)
{
int x = getint() + 1, y = getint() + 1;
++lev[Sum(x)]; Ins(x);
}
for (int i=0; i<n; ++i)
printf("%d\n", lev[i]);
return 0;
}
#undef lowbit
省选模板
最新推荐文章于 2020-08-26 16:28:13 发布