是时候总结一下了~
文章目录
其他
离散化
int getid(int x){return lower_bound(v.begin(),v.end(),x) - v.begin() + 1;}
for(int i = 1;i<=n;++i) scanf("%d",&a[i]), v.push_back(a[i]);
sort(v.begin(),v.end()), v.erase(unique(v.begin(),v.end()),v.end());
位运算
统计1的个数
int NumberOfOne(int n) {
int count = 0;
while(n) { n &= (n-1); count++;}
return count;
}
字符串
KMP
void getnext(){
nxt[0] = -1;
int k = -1;
for(int i = 1;i<=lens-1;++i){
while(k>-1 && str[k+1] != str[i]) k = nxt[k];
if(str[k+1] == str[i]) k++;
nxt[i] = k;
}
}
void kmp(){
getnext();
int k = -1;
for(int i = 0;i<=lent-1;++i){
while(k>-1&&str[k+1]!=text[i]) k = nxt[k];
if(str[k+1] == text[i])k++;
if(k==lens-1){
ans ++;
k = -1;
}
}
}
Hash
typedef unsigned long long ull;
const ull p = 67;
const ull MOD = 1610612741;
const ull MOD2 = 4294967291;
ull hashstr, pp[nmax] = {0,p};
char str[nmax];
int lenstr;
// 自然溢出
ull hashsub(int l, int r) {
return hashstr[r] - hashstr[l-1] * pp[r-l+1];
}
void getHash() {
for (int i = 2; i < nmax; ++i)
pp[i] = pp[i-1] * p;
for (int i = 1; i <= lenstr; ++i)
hashstr[i] = hashstr[i-1] * p + str[i];
}
// 带取模
ull hashsubmod(int l , int r) {
return ((hashstr[r] - hashstr[l-1] * pp[r-l+1]) % MOD + MOD) % MOD;
}
void getHashMod() {
for (int i = 2; i < nmax; ++i)
pp[i] = (pp[i-1] * p) % MOD;
for (int i = 1; i <= lenstr; ++i)
hashstr[i] = (hashstr[i-1] * p + str[i]) % MOD;
}
字典树
struct Trie {
int ch[maxlength][sigma_size];
int val[maxlength];
int sz;
init() {
sz = 1;
memset(ch[0], 0, sizeof(ch[0]));
}
inline 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] == 0) {
memset(ch[sz], 0, sizeof(ch[sz]));
ch[u][c] = sz++;
}
u = ch[u][c];
val[u] ++;
}
}
int search(char *s, int v) {
int u = 0, n = strlen(s), c;
for(int i = 0; i < n; i++) {
c = idx(s[i]);
if(ch[u][c] == 0) return 0;
u = ch[u][c];
}
return val[u];
}
}trie;
AC自动机
/* 解释:
nmax是自动机上所有字符的总长度,sigma是字符集大小
fail表示失配指针,last表示后缀链接
node[x].isocc表示当前字符是否为单词的结点,node[x].id表示加入自动机的单词编号
isocc和id可以根据题目做相应调整
调用:
aho.init()完成初始化,aho.insert(str)插入单词,aho.build_fail()创建失败指针
最后使用aho.match(str)完成匹配
*/
const int nmax = 50007;
const int sigma = 128;
struct Aho {
int sz;
queue<int> que;
struct Node {
int nxt[sigma];
bool isocc;
int fail, id, last;
} node[nmax];
void init() {
memset(node[0].nxt, 0, sizeof node[0].nxt);
sz = 1;
}
inline int idx(const char & ch) {return ch;}
void insert(char * S, int id) {
int len = strlen(S);
int now = 0;
for(int i = 0; i < len; ++i) {
int c = idx(S[i]);
if(!node[now].nxt[c]) {
memset(node[sz].nxt, 0, sizeof node[sz].nxt);
node[sz].fail = node[now].isocc = node[sz].isocc = 0;
node[now].nxt[c] = sz++;
}
now = node[now].nxt[c];
}
node[now].isocc = true;
node[now].id = id;
}
void build_fail() {
node[0].fail = 0;
node[0].last = 0;
for(int i = 0; i < sigma; ++i) {
if(node[0].nxt[i]) {
node[node[0].nxt[i]].fail = 0;
node[node[0].nxt[i]].last = 0;
que.push(node[0].nxt[i]);
}
}
while(!que.empty()) {
int now = que.front(); que.pop();
for(int i = 0; i < sigma; ++i) {
int son = node[now].nxt[i];
if(!son) {
node[now].nxt[i] = node[node[now].fail].nxt[i];
continue;
}
que.push(son);
node[son].fail = node[node[now].fail].nxt[i];
node[son].last = node[node[now].fail].isocc ? node[son].fail : node[node[son].fail].last;
}
}
}
void getans(int u) {
while(u) {
if(node[u].isocc)
occ_times[node[u].id] ++;
u = node[u].last;
}
}
void match(char * S) {
int len = strlen(S);
int now = 0;
for(int i = 0; i < len; ++i) {
int c = idx(S[i]);
now = node[now].nxt[c];
if(node[now].isocc) {
getans(now);
} else if(node[now].last) {
getans(node[now].last);
}
}
}
} aho;
树结构
DFS序
/* 解释:
pre[x]是原树编号->线段树编号的hash,[pre[x],suf[x]]是原树x表示的区间
hashback[x]是线段树编号->原树编号的hash
调用:
直接dfs(1,-1)即可将树形结构转换成线性结构
注意:
建立线段树的时,初始化有2种方法
1. 建树后直接单点更新,枚举原树一点i,update(pre[i]);
2. 建树时赋初值,tree[rt].val = initval[hashback[tree[rt].l]];
关键是原树编号和线段树编号别弄混了
线段树区间更新的时候,对于原树一点x,update(pre[x],suf[x]);
*/
int pre[nmax], suf[nmax], dfsnum, hashback[nmax], tot;
struct node {int to, nxt;} e[nmax << 1];
void add_edge(int u, int v) { e[tot].to = v, e[tot].nxt = head[u], head[u] = tot++;}
void dfs(int u, int f) {
pre[u] = ++dfsnum; sz[u] = 1, hashback[dfsnum] = u;
for (int i = head[u]; i != -1; i = e[i].nxt) if (e[i].to != f) dfs(e[i].to, u);
suf[u] = dfsnum;
}
树链剖分
/* 解释:
x的父亲fa[x],x的重儿子son[x],x的深度dep[x],x所在链的顶top[x],x的大小sz[x]
x的新编号newid[x],原来编号x = hashback[newid[x]],
调用:
dfsFirst(1,0,1);
dfsSecond(1,1);
*/
int fa[nmax], son[nmax], sz[nmax], newid[nmax], hashback[nmax], dep[nmax], top[nmax], head[nmax];
int num, tot;
struct edge {int to, nxt;} e[nmax << 1];
void add(int u, int v) { e[tot].to = v, e[tot].nxt = head[u], head[u] = tot++;}
void init() {
memset(head, -1, sizeof head);
memset(son, -1, sizeof son);
memset(hashback, 0, sizeof hashback);
tot = num = 0;
}
void dfsFirst(int u, int f, int d) {
dep[u] = d, fa[u] = f, sz[u] = 1;
for (int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if (v != f) {
dfsFirst(v, u, d + 1);
sz[u] += sz[v];
if (son[u] == -1 || sz[v] > sz[son[u]]) son[u] = v;
}
}
}
void dfsSecond(int u, int tp) {
top[u] = tp, newid[u] = ++num, hashback[num] = u;
if (son[u] == -1) return;
dfsSecond(son[u], tp);
for (int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if (v != son[u] && v != fa[u])
dfsSecond(v, v);
}
}
树上启发合并
/*
调用:
dfs1(1,-1,1)进行第一次dfs求出size和重儿子,dfs2(1,-1)统计答案
注意:
在dfs1的时候可以修改,适当做出调整,统计需要的信息,具体情况具体分析
*/
int tot,head[nmax],deep[nmax],sz[nmax],son[nmax];
bool visit[nmax];
struct edge {int to,nxt;} e[nmax<<1];
void add_edge(int u, int v) {e[tot].to = v; e[tot].nxt = head[u]; head[u] = tot++;}
void dfs1(int u, int f, int d) {
deep[u] = d; sz[u] = 1;
for(int i = head[u]; i!=-1; i = e[i].nxt) {
int v = e[i].to;
if(v != f) {
dfs1(v,u,d+1); sz[u] += sz[v];
if(sz[v] > sz[son[u]]) son[u] = v;
}
}
}
void update(int u, int f){
// 统计答案
cnt[deep[u]] ^= (1<<(letter[u] - 'a'));
// 遍历儿子
for(int i = head[u];i!=-1;i=e[i].nxt){
int v = e[i].to;
if(v != f && !visit[v]) update(v,u);
}
}
void dfs2(int u, int f, bool keep = false) {
for(int i = head[u]; i!=-1; i = e[i].nxt) {
int v = e[i].to;
if(v == f || v == son[u]) continue;
else dfs2(v,u);
}
if(son[u]) dfs2(son[u],u,true), visit[son[u]] = true;
// 计算当前节点答案
update(u,f);
// 统计答案:可以是直接统计,也可以是遍历所有询问统计
// 遍历所有询问:for(int i = qhead[u] ; i!=-1; i = query[i].nxt) query[i].ans = countone(cnt[query[i].to]);
// 直接统计: ans[i] = nowans;
if(son[u]) visit[son[u]] = false;
// 如果是轻儿子,则清楚轻儿子的答案
if(!keep) update(u,f);
}
左偏树
struct node {
int val, lc, rc, dis, fa;
}tree[nmax];
int tot = 0;
void init(int x) {
for (int i = 0; i <= x; ++i) {
tree[i].lc = tree[i].rc = tree[i].dis = 0;
tree[i].fa = i;
}
}
int merge(int x, int y) {
if (x == 0) return y;
if (y == 0) return x;
if (tree[x].val > tree[y].val || (tree[x].val == tree[y].val && x > y) )
swap(x, y);
tree[x].rc = merge(tree[x].rc, y);
tree[tree[x].rc].fa = x;
if (tree[tree[x].rc].dis > tree[tree[x].lc].dis)
swap(tree[x].rc, tree[x].lc);
tree[x].dis = tree[x].rc == 0 ? 0 : tree[tree[x].rc].dis + 1;
return x;
}
int findset(int x) {
while (tree[x].fa != x) {
x = tree[x].fa;
}
return x;
}
int add(int val, int x) {
tree[tot].lc = tree[tot].rc = tree[tot].dis = 0;
tree[tot++].val = val;
return merge(tot - 1, x);
}
int del(int x) {
int l = tree[x].lc, r = tree[x].rc;
tree[x].fa = tree[x].lc = tree[x].rc = tree[x].dis = 0;
tree[x].val = -INF;
tree[l].fa = l, tree[r].fa = r;
return merge(l, r);
}
int build() {
queue<int> q;
for (int i = 1; i <= n; ++i)
q.push(i);
while (!q.empty()) {
if (q.size() == 1) break;
else {
int x = q.front(); q.pop();
int y = q.front(); q.pop();
q.push(merge(x, y));
}
}
int finally = q.front(); q.pop();
return finally;
}
图论部分
欧拉回路与欧拉路径
无向图
首先要求图连通。
欧拉路径:图中度为奇数的有0个或者2个。
欧拉回路:图中没有度为奇数的点。
有向图
弱连通,也为底图连通,即去掉边的方向之后连通。
欧拉路径:有且只有2个点的入度不等于出度,其余点入度均等于出度。有一个点入度-出度=1,另一个点出度-入度-1,出度大的点为起点。
欧拉回路:所有点入度等于出度。
注意输出欧拉路径的时候,先dfs再输出,输出的时候要逆序。
‘’
强连通分量
/*解释:
color表示染色,sz表示每个连通分量的大小,sccnum表示强连通分量个数
in和out用于统计缩点后的图每个连通分量的入度和出度
调用:
首先调用init(vertexnum),之后add_edge(u,v)加边,最后start(运行算法)
如果必要可以调用get_ans()来对缩点后的图进行遍历,统计答案
*/
struct Tarjan {
int head[nmax], low[nmax], dfn[nmax], sstack[nmax], color[nmax], sz[nmax], top, sccnum, dfnnum, tot, n;
int in[nmax], out[nmax];
bool visit[nmax];
struct edge {int to, nxt;} e[nmax << 1];
void init(int n) {
memset(head, -1, sizeof head);
memset(mincost, INF, sizeof mincost);
memset(dfn, 0, sizeof dfn);
memset(color, 0, sizeof color);
memset(visit, 0, sizeof visit);
memset(sz, 0, sizeof sz);
memset(in, 0, sizeof in);
memset(out, 0, sizeof out);
cnt = dfnnum = top = sccnum = tot = 0;
this->n = n;
}
void add_edge(int u, int v) {
e[tot].to = v, e[tot].nxt = head[u]; head[u] = tot++;
}
void tarjan(int u) {
low[u] = dfn[u] = ++dfnnum;
visit[u] = true;
sstack[++top] = u;
for (int i = head[u] ; i != -1; i = e[i].nxt) {
int v = e[i].to;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (visit[v]) low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
visit[u] = false;
color[u] = ++ sccnum;
// printf("scc %d %d",sccnum,u);
sz[sccnum]++;
mincost[sccnum] = min(mincost[sccnum], cost[u]);
while (sstack[top] != u) {
// printf(" %d",sstack[top]);
color[sstack[top]] = sccnum;
sz[sccnum]++;
visit[sstack[top]] = false;
mincost[sccnum] = min(mincost[sccnum], cost[sstack[top]]);
top--;
}
// printf("\n");
top--;
}
}
void dfs(int u) {
visit[u] = true;
for (int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if (!visit[v]) {
if (color[v] != color[u]) in[color[v]]++, out[color[u]]++;
dfs(v);
} else if (visit[v]) {
if (color[v] != color[u]) in[color[v]]++, out[color[u]]++;
}
}
}
void start() {for (int i = 1; i <= n; ++i) if (!dfn[i]) tarjan(i);}
void get_ans() {
memset(visit, 0, sizeof visit);
for (int i = 1; i <= n; ++i) if (!visit[i]) dfs(i);
for (int i = 1; i <= sccnum; ++i) {
//统计答案
}
}
} solver;
割点
/*解释:
如果为割点iscut[i] = 1, cutnum[i]表示去掉点i新增的(连通分量数目值-1)
scc表示整张图的连通分量个数,n表示图的点数
调用:
先init(vertexnum);之后add_edge(u,v)加边;最后start()
*/
struct edge{
int nxt,to;
};
struct Cut_Tarjan{
int dfn[nmax],low[nmax],cutnum[nmax],head[nmax],dfs_clock,n,scc,tot;
bool iscut[nmax];
edge e[nmax<<1];
void add_edge(int u, int v){
e[tot].to = v, e[tot].nxt = head[u];
head[u] = tot++;
}
void init(int n){
memset(head,-1,sizeof head);
memset(dfn,0,sizeof dfn);
memset(cutnum,0,sizeof cutnum);
memset(iscut,0,sizeof iscut);
dfs_clock = tot = scc = 0;
this->n = n;
}
void dfs(int u,int fa){
int child = 0;
low[u] = dfn[u] = ++dfs_clock;
for(int i = head[u];i!=-1;i = e[i].nxt){
int v = e[i].to;
if(v != fa){
if(!dfn[v]){
child ++;
dfs(v,u);
low[u] = min(low[u],low[v]);
if(fa != -1 && low[v] >= dfn[u]) cutnum[u] ++,iscut[u] = true;
}else if(dfn[v]<low[u]) low[u] = min(low[u],dfn[v]);
}
}
if(fa == -1 && child > 1) iscut[u] = true;
if(fa == -1) cutnum[u] = child - 1;
}
void start(){
for(int i = 1;i<=n;++i) if(!dfn[i]) dfs(i,-1),scc++;
}
}cut_t;
网络流最大流
/*解释:
iscut用来判断是否是最小割,方法如下
在残量网络中,容量大于0表示可以走。将从源点出发可以到达的点看作S集,剩下的看作T集。
如果边(u,v)满足u属于S集,v属于T集,那么该边就是最小割边集中的边。
n为网络中的点数(如果0在图中的话不算),cur是当前弧优化
调用:
首先init(vertexnum);之后add_edge(u,v,c)建图;最后Maxflow(S,T);需要时可以getcut。
注意:
如果流量超过了int,记得换long long.
*/
struct Dinic {
int head[nmax], cur[nmax], d[nmax];
bool vis[nmax],iscut[nmax];
int tot, n, m, s, t;
struct edge {
int nxt, to, w, cap, flow;
} e[nmax<<1];
void init(int n) {
this->n = n;
this->tot = 0;
memset(head, -1, sizeof head);
memset(iscut,0,sizeof iscut);
}
void add_edge(int u, int v, int c) {
e[tot].to = v, e[tot].cap = c, e[tot].flow = 0;
e[tot].nxt = head[u];
head[u] = tot++;
e[tot].to = u, e[tot].cap = c, e[tot].flow = c;
e[tot].nxt = head[v];
head[v] = tot++;
}
bool BFS() {
memset(vis, 0, sizeof(vis));
queue<int>Q;
vis[s] = 1; d[s] = 0;
Q.push(s);
while (!Q.empty()) {
int u = Q.front(); Q.pop();
for (int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if (!vis[v] && e[i].cap > e[i].flow) {
vis[v] = 1;
d[v] = d[u] + 1;
Q.push(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 != -1; i = e[i].nxt) {
int v = e[i].to;
if (d[v] == d[x] + 1 && (f = DFS(v, min(a, e[i].cap - e[i].flow))) > 0) {
Flow += f;
e[i].flow += f;
e[i ^ 1].flow -= f;
a -= f;
if (a == 0) break;
}
}
return Flow;
}
int Maxflow(int s, int t) {
this->s = s, this->t = t;
int Flow = 0;
while (BFS()) {
for (int i = 0; i <= n; i++) cur[i] = head[i];
while(int once = DFS(s, INF)) Flow += once;
}
return Flow;
}
void get_cut(int u){
iscut[u] = true;
for (int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if (!iscut[v] && e[i].cap > e[i].flow)
get_cut(v);
}
return;
}
} dinic;
最小费用最大流
typedef long long valtype;
struct MCMF{
valtype final_flow,final_cost;
int tot,S,T;
bool inque[nmax];
int head[nmax], pre_edge[nmax],pre_index[nmax];
valtype add_flow[nmax], dis[nmax];
struct edge{int to,nxt;valtype cap,flow,cost;}e[nmax<<1];
void init(int S, int T){
memset(head,-1,sizeof head);
tot = 0;
this->S = S, this->T = T;
}
void add_edge(int u, int v, valtype cap, valtype cost){
e[tot].to = v, e[tot].nxt = head[u], e[tot].flow = 0, e[tot].cap = cap, e[tot].cost = cost, head[u] = tot++;
e[tot].to = u, e[tot].nxt = head[v], e[tot].flow = 0, e[tot].cap = 0, e[tot].cost = -cost, head[v] = tot++;
}
bool spfa(){
for(int i = S;i<=T;++i) inque[i] = false, dis[i] = i == S?0:INF;
queue<int> q; q.push(S), inque[S] = true, add_flow[S] = INF;
while(!q.empty()){
int u = q.front(); q.pop(); inque[u] = false;
for(int i = head[u];i!=-1;i=e[i].nxt){
int v = e[i].to;
if(e[i].cap > e[i].flow && dis[u] + e[i].cost < dis[v]){
dis[v] = dis[u] + e[i].cost, pre_edge[v] = i, pre_index[v] = u;
add_flow[v] = min(add_flow[u],e[i].cap - e[i].flow);
if(!inque[v]) q.push(v),inque[v] = true;
}
}
}
return dis[T] != INF;
}
void mincost_mxflow() {
final_cost = final_flow = 0;
while(spfa()){
final_flow += add_flow[T];
final_cost += add_flow[T] * dis[T];
int now = T;
while(now != S){
e[pre_edge[now]].flow += add_flow[T];
e[pre_edge[now]^1].flow -= add_flow[T];
now = pre_index[now];
}
}
}
}mcmf;
zkw最小费用最大流
/* 注意:点的编号不能为0
*/
typedef int valtype;
struct zkwflow {
struct edge {
valtype cost, cap;
int nxt, re, to;
}e[nmax];
int head[nmax], tot, vis[nmax], s, t;
valtype ans, cost, maxflow;
void init(int s, int t) {
memset(head, -1, sizeof(head));
tot = 0;
ans = cost = maxflow = 0;
this->s = s, this->t = t;
}
void add_edge(int u, int v, valtype cap, valtype cost) {
e[tot].to = v;
e[tot].cap = cap;
e[tot].cost = cost;
e[tot].re = tot + 1;
e[tot].nxt = head[u];
head[u] = tot++;
e[tot].to = u;
e[tot].cap = 0;
e[tot].cost = -cost;
e[tot].re = tot - 1;
e[tot].nxt = head[v];
head[v] = tot++;
}
valtype aug(int u, valtype f) {
if(u == t) {
ans += cost * f;
maxflow += f;
return f;
}
vis[u] = 1;
valtype tmp = f;
for(int i = head[u]; i != -1; i = e[i].nxt)
if(e[i].cap && !e[i].cost && !vis[e[i].to]) {
valtype delta = aug(e[i].to, tmp < e[i].cap ? tmp : e[i].cap);
e[i].cap -= delta;
e[e[i].re].cap += delta;
tmp -= delta;
if(!tmp) return f;
}
return f - tmp;
}
bool modlabel(int n) {
valtype delta = INF;
for(int u = 1; u <= n; u++)
if(vis[u])
for(int i = head[u]; i != -1; i = e[i].nxt)
if(e[i].cap && !vis[e[i].to] && e[i].cost < delta) delta = e[i].cost;
if(delta == INF) return false;
for(int u = 1; u <= n; u++)
if(vis[u])
for(int i = head[u]; i != -1; i = e[i].nxt)
e[i].cost -= delta, e[e[i].re].cost += delta;
cost += delta;
return true;
}
valtype costflow() {
do {
do {
memset(vis, 0, sizeof(vis));
}while(aug(s, INF));
}while(modlabel(t));
return ans;
}
}zkw;
二分图最佳完美匹配
/*解释:见代码
调用:
初始化init(vertexnum);对edge直接进行修改;调用Perfect_Match()进行匹配。
注意:
默认求解最大权值匹配,如果需要求解最小,需要将edge取负,最后结果再取负即可。
*/
struct KM{
bool mp[size][size]; // 二分图的相等子图, mp[i][j] = true 代表Xi与Yj有边
bool xckd[size], yckd[size]; // 标记在一次DFS中,Xi与Yi是否在交错树上
int match[size]; // 保存匹配信息,其中i为Y中的顶点标号,match[i]为X中顶点标号
int n, edge[size][size]; // edge[i][j]为连接Xi与Yj的边的权值
void Perfect_Match() {
int i, j;
int lx[size], ly[size]; // KM算法中Xi与Yi的标号
for(i = 0; i < n; i++) {
lx[i] = -INF;
ly[i] = 0;
for(j = 0; j < n; j++)
lx[i] = max(lx[i], edge[i][j]);
}
bool perfect = false;
while(!perfect) {
for(i = 0; i < n; i++) // 初始化邻接矩阵
for(j = 0; j < n; j++)
if(lx[i]+ly[j] == edge[i][j]) mp[i][j] = true;
else mp[i][j] = false;
int live = 0; // 匹配过程
memset(match, -1, sizeof(match));
for(i = 0; i < n; i++) {
memset(xckd, false, sizeof(xckd));
memset(yckd, false, sizeof(yckd));
if(DFS(i, n))live++;
else {
xckd[i] = true;
break;
}
}
if(live == n) perfect = true;
else {
int ex = INF;// 修改标号过程
for(i = 0; i < n; i++)
for(j = 0; xckd[i] && j < n; j++)
if(!yckd[j]) ex = min(ex, lx[i]+ly[j]-edge[i][j]);
for(i = 0; i < n; i++) {
if(xckd[i]) lx[i] -= ex;
if(yckd[i]) ly[i] += ex;
}
}
}
}
bool DFS(int p, const int n) { // 此函数用来寻找是否有以Xp为起点的增广路径,返回值为是否含有增广路
for(int i = 0; i < n; i++) {
if(!yckd[i] && mp[p][i]) {
yckd[i] = true;
int t = match[i];
match[i] = p;
if(t == -1 || DFS(t, n)) return true;
match[i] = t;
if(t != -1) xckd[t] = true;
}
}
return false;
}
void init(int n){
memset(edge,0,sizeof edge);
this->n = n;
}
}km;
数学部分
线性基
/* 解释:
iszero表示线性基中是否异或后是否有0
使用:
insert(x)向线性基中插入数值,如果需要查询第k大,需要rebuild()
*/
struct L_B {
ll d[61], p[61];
int cnt;
bool iszero;
L_B() {
memset(d, 0, sizeof(d));
memset(p, 0, sizeof(p));
iszero = cnt = 0;
}
void clear() {
memset(d, 0, sizeof(d));
memset(p, 0, sizeof(p));
iszero = cnt = 0;
}
bool insert(ll val) {
for (int i = 60; i >= 0; i--)
if (val & (1LL << i)) {
if (!d[i]) { d[i] = val; break;}
val ^= d[i];
}
if (val == 0) iszero = true;
return val > 0;
}
ll query_max() {
ll ret = 0;
for (int i = 60; i >= 0; i--) if ((ret ^ d[i]) > ret) ret ^= d[i];
return ret;
}
ll query_min() {
if (iszero) return 0LL;
for (int i = 0; i <= 60; i++) if (d[i]) return d[i];
return 0;
}
void rebuild() {
for (int i = 60; i >= 0; i--)
for (int j = i - 1; j >= 0; j--)
if (d[i] & (1LL << j)) d[i] ^= d[j];
for (int i = 0; i <= 60; i++) if (d[i]) p[cnt++] = d[i];
}
ll kthquery(ll k) {
if (iszero) k--;
ll ret = 0;
if (k >= (1LL << cnt)) return -1;
for (int i = 60; i >= 0; i--) if (k & (1LL << i)) ret ^= p[i];
return ret;
}
} lb;
离线算法
普通莫队
/* 解释:
belong[x]x属于分块后的哪一块,Q[i]每个询问
modify(p,t)对p位置进行t修改,一般只有增加或者缩减这两种操作,具体问题具体分析
注意:
最后也可以不对询问id排序,直接保存到一个数组里面输出即可
*/
int a[nmax], belong[nmax];
ll ans = 0;
struct node {int l, r, id;ll ans;} Q[nmax];
bool cmp(node a, node b) {
if (belong[a.l] != belong[b.l]) return a.l < b.l;
else return a.r < b.r;
}
bool cmpid(node a, node b) {return a.id < b.id;}
void modify(int pos, int tag) {
// ......... 增删操作
}
int main() {
scanf("%d %d", &n, &m);
int sz = sqrt(n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= m; ++i) {
scanf("%d %d", &Q[i].l, &Q[i].r), Q[i].id = i;
belong[i] = (i - 1) / sz + 1;
}
sort(Q + 1, Q + 1 + m, cmp);
int l = 1, r = 0;
for (int i = 1; i <= m; ++i) {
while (l < Q[i].l) modify(l++, -1);
while (l > Q[i].l) modify(--l, 1);
while (r > Q[i].r) modify(r--, -1);
while (r < Q[i].r) modify(++r, 1);
Q[i].ans = ans;
}
sort(Q + 1, Q + 1 + m, cmpid);
for (int i = 1; i <= m; ++i) printf("%I64d\n", Q[i].ans);
return 0;
}
RMQ
int dp[nmax][(int) (log2(nmax) + 10)];
int a[nmax];
void getRMQ() {
for (int i = 1; i <= n; ++i)
dp[i][0] = a[i];
for (int j = 1; (1 << j) <= n ; ++j)
for (int i = 1; i + ( 1 << j ) - 1 <= n; ++i)
dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1 ))][j - 1]);
}
int RMQmax(int l, int r) {
int k = 0;
while ( (1 << (k + 1)) <= r - l + 1) ++k;
return max(dp[l][k], dp[r - (1 << k) + 1][k]);
}