ret+=k;
p=l1[s[k]]+l2[j]-mat[s[k]][j];
return ret;
}
void num2perm(int n,int *p,int t){
int i,j;
for (i=n-1;i>=0;i--)
p[i]=t%(n-i),t/=n-i;
for (i=n-1;i;i--)
for (j=i-1;j>=0;j--)
if (p[j]<=p[i])
p[i]++;
}
2.6 字典序组合
//字典序组合与序号的转换
//comb为组合数 C(n,m),必要时换成大数,注意处理C(n,m)=0|n
int ret=1,i;
m=m<(n-m)?m:(n-m);
for (i=n-m+1;i<=n;ret*=(i++));
for (i=1;i<=m;ret/=(i++));
return m<0?0:ret; 58
}
int comb2num(int n,int m,int *c){
int ret=comb(n,m),i;
for (i=0;i
return ret;
}
void num2comb(int n,int m,int* c,int t){
int i,j=1,k;
for (i=0;i
}
3、 结构
3.1 并查集
//带路径压缩的并查集,用于动态维护查询等价类
//图论算法中动态判点集连通常用
//维护和查询复杂度略大于 O(1)
//集合元素取值 1..MAXN-1(注意0 不能用!),默认不等价
#include
#define MAXN 100000
#define _ufind_run(x) for(;p[t=x];x=p[x],p[t]=(p[x]?p[x]:x))
#define _run_both _ufind_run(i);_ufind_run(j)
struct ufind{
int p[MAXN],t;
void init(){memset(p,0,sizeof(p));}
void set_friend(int i,int j){_run_both;p[i]=(i==j?0:j);}
int is_friend(int i,int j){_run_both;return i==j&&i;}
};
//带路径压缩的并查集扩展形式
//用于动态维护查询friend-enemy型等价类
//维护和查询复杂度略大于 O(1)
//集合元素取值 1..MAXN-1(注意0不能用!),默认无关
#include
#define MAXN 100000
#define sig(x) ((x)>0?1:-1) 59
#define abs(x) ((x)>0?(x):-(x))
#define _ufind_run(x)
for(;p[t=abs(x)];x=sig(x)*p[abs(x)],p[t]=sig(p[t])*(p[abs(x)]?p[abs(x)]:abs(p[t])))
#define _run_both _ufind_run(i);_ufind_run(j)
#define _set_side(x) p[abs(i)]=sig(i)*(abs(i)==abs(j)?0:(x)*j)
#define _judge_side(x) (i==(x)*j&&i)
struct ufind{
int p[MAXN],t;
void init(){memset(p,0,sizeof(p));}
int set_friend(int i,int j){_run_both;_set_side(1);return !_judge_side(-1);}
int set_enemy(int i,int j){_run_both;_set_side(-1);return !_judge_side(1);}
int is_friend(int i,int j){_run_both;return _judge_side(1);}
int is_enemy(int i,int j){_run_both;return _judge_side(-1);}
};
3.2 堆
//二分堆(binary)
//可插入,获取并删除最小(最大)元素,复杂度均O(logn)
//可更改元素类型,修改比较符号或换成比较函数
#define MAXN 10000
#define _cp(a,b) ((a)<(b))
typedef int elem_t;
struct heap{
elem_t h[MAXN];
int n,p,c;
void init(){n=0;}
void ins(elem_t e){
for (p=++n;p>1&&_cp(e,h[p>>1]);h[p]=h[p>>1],p>>=1);
h[p]=e;
}
int del(elem_t& e){
if (!n) return 0;
for
(e=h[p=1],c=2;c
}
};
//映射二分堆(mapped)
//可插入,获取并删除任意元素,复杂度均O(logn)
//插入时提供一个索引值,删除时按该索引删除,获取并删除最小元素时一起获得该索引 60
//索引值范围 0..MAXN-1,不能重复,不负责维护索引的唯一性,不在此返回请另外映射
//主要用于图论算法,该索引值可以是节点的下标
//可更改元素类型,修改比较符号或换成比较函数
#define MAXN 10000
#define _cp(a,b) ((a)<(b))
typedef int elem_t;
struct heap{
elem_t h[MAXN];
int ind[MAXN],map[MAXN],n,p,c;
void init(){n=0;}
void ins(int i,elem_t e){
for (p=++n;p>1&&_cp(e,h[p>>1]);h[map[ind[p]=ind[p>>1]]=p]=h[p>>1],p>>=1);
h[map[ind[p]=i]=p]=e;
}
int del(int i,elem_t& e){
i=map[i];if (i<1||i>n) return 0;
for (e=h[p=i];p>1;h[map[ind[p]=ind[p>>1]]=p]=h[p>>1],p>>=1);
for
(c=2;c
h[map[ind[p]=ind[n]]=p]=h[n];n--;return 1;
}
int delmin(int& i,elem_t& e){
if (n<1) return 0;i=ind[1];
for
(e=h[p=1],c=2;c
h[map[ind[p]=ind[n]]=p]=h[n];n--;return 1;
}
};
3.3 线段树
线段树应用:
求面积:
1) 坐标离散化
2) 垂直边按x坐标排序
3) 从左往右用线段树处理垂直边
累计每个离散x 区间长度和线段树长度的乘积
求周长:
1) 坐标离散化
2) 垂直边按x 坐标排序, 第二关键字为入边优于出边
3) 从左往右用线段树处理垂直边 61
在每个离散点上先加入所有入边, 累计线段树长度变化值
再删除所有出边, 累计线段树长度变化值
4) 水平边按y坐标排序, 第二关键字为入边优于出边
5) 从上往下用线段树处理水平边
在每个离散点上先加入所有入边, 累计线段树长度变化值
再删除所有出边, 累计线段树长度变化值
//线段树
//可以处理加入边和删除边不同的情况
//inc_seg和 dec_seg用于加入边
//seg_len求长度
//t传根节点(一律为1)
//l0,r0 传树的节点范围(一律为1..t)
//l,r 传线段(端点)
#define MAXN 10000
struct segtree{
int n,cnt[MAXN],len[MAXN];
segtree(int t):n(t){
for (int i=1;i<=t;i++)
cnt[i]=len[i]=0;
};
void update(int t,int l,int r);
void inc_seg(int t,int l0,int r0,int l,int r);
void dec_seg(int t,int l0,int r0,int l,int r);
int seg_len(int t,int l0,int r0,int l,int r);
};
int length(int l,int r){
return r-l;
}
void segtree::update(int t,int l,int r){
if (cnt[t]||r-l==1)
len[t]=length(l,r);
else
len[t]=len[t+t]+len[t+t+1];
}
void segtree::inc_seg(int t,int l0,int r0,int l,int r){
if (l0==l&&r0==r)
cnt[t]++;
else{
int m0=(l0+r0)>>1;
if (l
inc_seg(t+t+1,m0,r0,m0>l?m0:l,r);
if (cnt[t+t]&&cnt[t+t+1]){
cnt[t+t]--;
update(t+t,l0,m0);
cnt[t+t+1]--;
update(t+t+1,m0,r0);
cnt[t]++;
}
}
update(t,l0,r0);
}
void segtree::dec_seg(int t,int l0,int r0,int l,int r){
if (l0==l&&r0==r)
cnt[t]--;
else if (cnt[t]){
cnt[t]--;
if (l>l0)
inc_seg(t,l0,r0,l0,l);
if (r
}
else{
int m0=(l0+r0)>>1;
if (l
dec_seg(t+t+1,m0,r0,m0>l?m0:l,r);
}
update(t,l0,r0);
}
int segtree::seg_len(int t,int l0,int r0,int l,int r){
if (cnt[t]||(l0==l&&r0==r))
return len[t];
else{
int m0=(l0+r0)>>1,ret=0;
if (l
ret+=seg_len(t+t+1,m0,r0,m0>l?m0:l,r);
return ret; 63
}
}
//线段树扩展
//可以计算长度和线段数
//可以处理加入边和删除边不同的情况
//inc_seg和 dec_seg用于加入边
//seg_len求长度,seg_cut求线段数
//t传根节点(一律为1)
//l0,r0 传树的节点范围(一律为1..t)
//l,r 传线段(端点)
#define MAXN 10000
struct segtree{
int n,cnt[MAXN],len[MAXN],cut[MAXN],bl[MAXN],br[MAXN];
segtree(int t):n(t){
for (int i=1;i<=t;i++)
cnt[i]=len[i]=cut[i]=bl[i]=br[i]=0;
};
void update(int t,int l,int r);
void inc_seg(int t,int l0,int r0,int l,int r);
void dec_seg(int t,int l0,int r0,int l,int r);
int seg_len(int t,int l0,int r0,int l,int r);
int seg_cut(int t,int l0,int r0,int l,int r);
};
int length(int l,int r){
return r-l;
}
void segtree::update(int t,int l,int r){
if (cnt[t]||r-l==1)
len[t]=length(l,r),cut[t]=bl[t]=br[t]=1;
else{
len[t]=len[t+t]+len[t+t+1];
cut[t]=cut[t+t]+cut[t+t+1];
if (br[t+t]&&bl[t+t+1])
cut[t]--;
bl[t]=bl[t+t],br[t]=br[t+t+1];
}
}
void segtree::inc_seg(int t,int l0,int r0,int l,int r){
if (l0==l&&r0==r)
cnt[t]++; 64
else{
int m0=(l0+r0)>>1;
if (l
inc_seg(t+t+1,m0,r0,m0>l?m0:l,r);
if (cnt[t+t]&&cnt[t+t+1]){
cnt[t+t]--;
update(t+t,l0,m0);
cnt[t+t+1]--;
update(t+t+1,m0,r0);
cnt[t]++;
}
}
update(t,l0,r0);
}
void segtree::dec_seg(int t,int l0,int r0,int l,int r){
if (l0==l&&r0==r)
cnt[t]--;
else if (cnt[t]){
cnt[t]--;
if (l>l0)
inc_seg(t,l0,r0,l0,l);
if (r
}
else{
int m0=(l0+r0)>>1;
if (l
dec_seg(t+t+1,m0,r0,m0>l?m0:l,r);
}
update(t,l0,r0);
}
int segtree::seg_len(int t,int l0,int r0,int l,int r){
if (cnt[t]||(l0==l&&r0==r))
return len[t];
else{
int m0=(l0+r0)>>1,ret=0;
if (l
ret+=seg_len(t+t+1,m0,r0,m0>l?m0:l,r);
return ret;
}
}
int segtree::seg_cut(int t,int l0,int r0,int l,int r){
if (cnt[t])
return 1;
if (l0==l&&r0==r)
return cut[t];
else{
int m0=(l0+r0)>>1,ret=0;
if (l
ret+=seg_cut(t+t+1,m0,r0,m0>l?m0:l,r);
if (l
ret--;
return ret;
}
}
3.4 子段和
//求 sum{[0..n-1]}
//维护和查询复杂度均为O(logn)
//用于动态求子段和,数组内容保存在 sum.a[]中
//可以改成其他数据类型
#include
#define lowbit(x) ((x)&((x)^((x)-1)))
#define MAXN 10000
typedef int elem_t;
struct sum{
elem_t a[MAXN],c[MAXN],ret;
int n;
void init(int i){memset(a,0,sizeof(a));memset(c,0,sizeof(c));n=i;}
void update(int i,elem_t v){for (v-=a[i],a[i++]+=v;i<=n;c[i-1]+=v,i+=lowbit(i));}
elem_t query(int i){for (ret=0;i;ret+=c[i-1],i^=lowbit(i));return ret;}
};
3.5 子阵和 66
//求 sum{a[0..m-1][0..n-1]}
//维护和查询复杂度均为O(logm*logn)
//用于动态求子阵和,数组内容保存在 sum.a[][]中
//可以改成其他数据类型
#include
#define lowbit(x) ((x)&((x)^((x)-1)))
#define MAXN 100
typedef int elem_t;
struct sum{
elem_t a[MAXN][MAXN],c[MAXN][MAXN],ret;
int m,n,t;
void init(int i,int j){memset(a,0,sizeof(a));memset(c,0,sizeof(c));m=i,n=j;}
void update(int i,int j,elem_t v){
for (v-=a[i][j],a[i++][j++]+=v,t=j;i<=m;i+=lowbit(i))
for (j=t;j<=n;c[i-1][j-1]+=v,j+=lowbit(j));
}
elem_t query(int i,int j){
for (ret=0,t=j;i;i^=lowbit(i))
for (j=t;j;ret+=c[i-1][j-1],j^=lowbit(j));
return ret;
}
};
4、 数论
4.1 阶乘最后非 0位
//求阶乘最后非零位,复杂度O(nlogn)
//返回该位,n以字符串方式传入
#include
#define MAXN 10000
int lastdigit(char* buf){
const int mod[20]={1,1,2,6,4,2,2,4,2,8,4,4,8,4,6,8,8,6,8,2};
int len=strlen(buf),a[MAXN],i,c,ret=1;
if (len==1)
return mod[buf[0]-'0'];
for (i=0;i
for (;len;len-=!a[len-1]){
ret=ret*mod[a[1]%2*10+a[0]]%5;
for (c=0,i=len-1;i>=0;i--) 67
c=c*10+a[i],a[i]=c/5,c%=5;
}
return ret+ret%2*5;
}
4.2 模线性方程组
#ifdef WIN32
typedef __int64 i64;
#else
typedef long long i64;
#endif
//扩展 Euclid求解gcd(a,b)=ax+by
int ext_gcd(int a,int b,int& x,int& y){
int t,ret;
if (!b){
x=1,y=0;
return a;
}
ret=ext_gcd(b,a%b,x,y);
t=x,x=y,y=t-a/b*y;
return ret;
}
//计算 m^a, O(loga), 本身没什么用, 注意这个按位处理的方法 :-P
int exponent(int m,int a){
int ret=1;
for (;a;a>>=1,m*=m)
if (a&1)
ret*=m;
return ret;
}
//计算幂取模a^b mod n, O(logb)
int modular_exponent(int a,int b,int n){ //a^b mod n
int ret=1;
for (;b;b>>=1,a=(int)((i64)a)*a%n)
if (b&1)
ret=(int)((i64)ret)*a%n;
return ret;
}
//求解模线性方程ax=b (mod n)
//返回解的个数,解保存在 sol[]中 68
//要求 n>0,解的范围0..n-1
int modular_linear(int a,int b,int n,int* sol){
int d,e,x,y,i;
d=ext_gcd(a,n,x,y);
if (b%d)
return 0;
e=(x*(b/d)%n+n)%n;
for (i=0;i
return d;
}
//求解模线性方程组(中国余数定理)
// x = b[0] (mod w[0])
// x = b[1] (mod w[1])
// ...
// x = b[k-1] (mod w[k-1])
//要求 w[i]>0,w[i]与w[j]互质,解的范围1..n,n=w[0]*w[1]*...*w[k-1]
int modular_linear_system(int b[],int w[],int k){
int d,x,y,a=0,m,n=1,i;
for (i=0;i
for (i=0;i
d=ext_gcd(w[i],m,x,y);
a=(a+y*m*b[i])%n;
}
return (a+n)%n;
}
4.3 素数
//用素数表判定素数,先调用initprime
int plist[10000],pcount=0;
int prime(int n){
int i;
if ((n!=2&&!(n%2))||(n!=3&&!(n%3))||(n!=5&&!(n%5))||(n!=7&&!(n%7)))
return 0;
for (i=0;plist[i]*plist[i]<=n;i++)
if (!(n%plist[i]))
return 0;
return n>1;
} 69
void initprime(){
int i;
for (plist[pcount++]=2,i=3;i<50000;i++)
if (prime(i))
plist[pcount++]=i;
}
//miller rabin
//判断自然数 n是否为素数
//time越高失败概率越低,一般取10到 50
#include
#ifdef WIN32
typedef __int64 i64;
#else
typedef long long i64;
#endif
int modular_exponent(int a,int b,int n){ //a^b mod n
int ret;
for (;b;b>>=1,a=(int)((i64)a)*a%n)
if (b&1)
ret=(int)((i64)ret)*a%n;
return ret;
}
// Carmicheal number: 561,41041,825265,321197185
int miller_rabin(int n,int time=10){
if (n==1||(n!=2&&!(n%2))||(n!=3&&!(n%3))||(n!=5&&!(n%5))||(n!=7&&!(n%7)))
return 0;
while (time--)
if
(modular_exponent(((rand()&0x7fff<<16)+rand()&0x7fff+rand()&0x7fff)%(n-1)+1,n-1,n)!=1)
return 0;
return 1;
}
4.4 欧拉函数
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
inline int lcm(int a,int b){ 70
return a/gcd(a,b)*b;
}
//求 1..n-1 中与 n互质的数的个数
int eular(int n){
int ret=1,i;
for (i=2;i*i<=n;i++)
if (n%i==0){
n/=i,ret*=i-1;
while (n%i==0)
n/=i,ret*=i;
}
if (n>1)
ret*=n-1;
return ret;
}
5、 数值计算
5.1 定积分计算(Romberg)
/* Romberg求定积分
输入:积分区间[a,b],被积函数f(x,y,z)
输出:积分结果
f(x,y,z)示例:
double f0( double x, double l, double t )
{
return sqrt(1.0+l*l*t*t*cos(t*x)*cos(t*x));
}
*/
double Integral(double a, double b, double (*f)(double x, double y, double z), double eps,
double l, double t)
double Romberg (double a, double b, double (*f)(double x, double y, double z), double eps,
double l, double t)
{
#define MAX_N 1000
int i, j, temp2, min;
double h, R[2][MAX_N], temp4;
71
for (i=0; i
R[1][i] = 0.0;
}
h = b-a;
min = (int)(log(h*10.0)/log(2.0)); //h should be at most 0.1
R[0][0] = ((*f)(a, l, t)+(*f)(b, l, t))*h*0.50;
i = 1;
temp2 = 1;
while (i
R[1][0] = 0.0;
for (j=1; j<=temp2; j++)
R[1][0] += (*f)(a+h*((double)j-0.50), l, t);
R[1][0] = (R[0][0] + h*R[1][0])*0.50;
temp4 = 4.0;
for (j=1; j R[1][j] = R[1][j-1] + (R[1][j-1]-R[0][j-1])/(temp4-1.0);
temp4 *= 4.0;
}
if ((fabs(R[1][i-1]-R[0][i-2])
return R[1][i-1];
h *= 0.50;
temp2 *= 2;
for (j=0; j R[0][j] = R[1][j];
}
return R[1][MAX_N-1];
}
double Integral(double a, double b, double (*f)(double x, double y, double z), double eps,
double l, double t)
{
#define pi 3.1415926535897932
int n;
double R, p, res;
n = (int)(floor)(b * t * 0.50 / pi);
p = 2.0 * pi / t;
res = b - (double)n * p;
if (n)
R = Romberg (a, p, f0, eps/(double)n, l, t);
R = R * (double)n + Romberg( 0.0, res, f0, eps, l, t ); 72
return R/100.0;
}
5.2 多项式求根(牛顿法)
/* 牛顿法解多项式的根
输入:多项式系数 c[],多项式度数 n,求在[a,b]间的根
输出:根
要求保证[a,b]间有根
*/
double fabs( double x )
{
return (x<0)? -x : x;
}
double f(int m, double c[], double x)
{
int i;
double p = c[m];
for (i=m; i>0; i--)
p = p*x + c[i-1];
return p;
}
int newton(double x0, double *r,
double c[], double cp[], int n,
double a, double b, double eps)
{
int MAX_ITERATION = 1000;
int i = 1;
double x1, x2, fp, eps2 = eps/10.0;
x1 = x0;
while (i < MAX_ITERATION) {
x2 = f(n, c, x1);
fp = f(n-1, cp, x1);
if ((fabs(fp)<0.000000001) && (fabs(x2)>1.0))
return 0;
x2 = x1 - x2/fp;
if (fabs(x1-x2)
return 0;
*r = x2;
return 1;
}
x1 = x2;
i++;
}
return 0;
}
double Polynomial_Root(double c[], int n, double a, double b, double eps)
{
double *cp;
int i;
double root;
cp = (double *)calloc(n, sizeof(double));
for (i=n-1; i>=0; i--) {
cp[i] = (i+1)*c[i+1];
}
if (a>b) {
root = a; a = b; b = root;
}
if ((!newton(a, &root, c, cp, n, a, b, eps)) &&
(!newton(b, &root, c, cp, n, a, b, eps)))
newton((a+b)*0.5, &root, c, cp, n, a, b, eps);
free(cp);
if (fabs(root)
else
return root;
}
5.3 周期性方程(追赶法)
/* 追赶法解周期性方程
周期性方程定义:| a1 b1 c1 ... | = x1
| a2 b2 c2 ... | = x2
| ... | * X = ...
| cn-1 ... an-1 bn-1 | = xn-1
| bn cn an | = xn
输入:a[],b[],c[],x[]
输出:求解结果X 在 x[]中
*/ 74
void run()
{
c[0] /= b[0]; a[0] /= b[0]; x[0] /= b[0];
for (int i = 1; i < N - 1; i ++) {
double temp = b[i] - a[i] * c[i - 1];
c[i] /= temp;
x[i] = (x[i] - a[i] * x[i - 1]) / temp;
a[i] = -a[i] * a[i - 1] / temp;
}
a[N - 2] = -a[N - 2] - c[N - 2];
for (int i = N - 3; i >= 0; i --) {
a[i] = -a[i] - c[i] * a[i + 1];
x[i] -= c[i] * x[i + 1];
}
x[N - 1] -= (c[N - 1] * x[0] + a[N - 1] * x[N - 2]);
x[N - 1] /= (c[N - 1] * a[0] + a[N - 1] * a[N - 2] + b[N - 1]);
for (int i = N - 2; i >= 0; i --)
x[i] += a[i] * x[N - 1];
}
6、 图论—NP搜索
6.1 最大团
//最大团
//返回最大团大小和一个方案,传入图的大小n和邻接阵 mat
//mat[i][j]为布尔量
#define MAXN 60
void clique(int n, int* u, int mat[][MAXN], int size, int& max, int& bb, int* res, int* rr, int* c) {
int i, j, vn, v[MAXN];
if (n) {
if (size + c[u[0]] <= max) return;
for (i = 0; i < n + size - max && i < n; ++ i) {
for (j = i + 1, vn = 0; j < n; ++ j)
if (mat[u[i]][u[j]])
v[vn ++] = u[j];
rr[size] = u[i];
clique(vn, v, mat, size + 1, max, bb, res, rr, c);
if (bb) return;
} 75
} else if (size > max) {
max = size;
for (i = 0; i < size; ++ i)
res[i] = rr[i];
bb = 1;
}
}
int maxclique(int n, int mat[][MAXN], int *ret) {
int max = 0, bb, c[MAXN], i, j;
int vn, v[MAXN], rr[MAXN];
for (c[i = n - 1] = 0; i >= 0; -- i) {
for (vn = 0, j = i + 1; j < n; ++ j)
if (mat[i][j])
v[vn ++] = j;
bb = 0;
rr[0] = i;
clique(vn, v, mat, 1, max, bb, ret, rr, c);
c[i] = max;
}
return max;
}
6.2 最大团(n<64)(faster)
/**
* WishingBone's ACM/ICPC Routine Library
*
* maximum clique solver
*/
#include
using std::vector;
// clique solver calculates both size and consitution of maximum clique
// uses bit operation to accelerate searching
// graph size limit is 63, the graph should be undirected
// can optimize to calculate on each component, and sort on vertex degrees
// can be used to solve maximum independent set
class clique {
public:
static const long long ONE = 1;
static const long long MASK = (1 << 21) - 1; 76
char* bits;
int n, size, cmax[63];
long long mask[63], cons;
// initiate lookup table
clique() {
bits = new char[1 << 21];
bits[0] = 0;
for (int i = 1; i < 1 << 21; ++i) bits[i] = bits[i >> 1] + (i & 1);
}
~clique() {
delete bits;
}
// search routine
bool search(int step, int size, long long more, long long con);
// solve maximum clique and return size
int sizeClique(vector
// solve maximum clique and return constitution
vector
};
// search routine
// step is node id, size is current solution, more is available mask, cons is
constitution mask
bool clique::search(int step, int size, long long more, long long cons) {
if (step >= n) {
// a new solution reached
this->size = size;
this->cons = cons;
return true;
}
long long now = ONE << step;
if ((now & more) > 0) {
long long next = more & mask[step];
if (size + bits[next & MASK] + bits[(next >> 21) & MASK] + bits[next >>
42] >= this->size
&& size + cmax[step] > this->size) {
// the current node is in the clique
if (search(step + 1, size + 1, next, cons | now)) return true;
}
}
long long next = more & ~now;
if (size + bits[next & MASK] + bits[(next >> 21) & MASK] + bits[next >> 42]
> this->size) {
// the current node is not in the clique 77
if (search(step + 1, size, next, cons)) return true;
}
return false;
}
// solve maximum clique and return size
int clique::sizeClique(vector
n = mat.size();
// generate mask vectors
for (int i = 0; i < n; ++i) {
mask[i] = 0;
for (int j = 0; j < n; ++j) if (mat[i][j] > 0) mask[i] |= ONE << j;
}
size = 0;
for (int i = n - 1; i >= 0; --i) {
search(i + 1, 1, mask[i], ONE << i);
cmax[i] = size;
}
return size;
}
// solve maximum clique and return constitution
// calls sizeClique and restore cons
vector
sizeClique(mat);
vector
for (int i = 0; i < n; ++i) if ((cons & (ONE << i)) > 0) ret.push_back(i);
return ret;
}
7、 图论—连通性
7.1 无向图关键点(dfs邻接阵)
//无向图的关键点,dfs邻接阵形式,O(n^2)
//返回关键点个数,key[]返回点集
//传入图的大小 n和邻接阵mat,不相邻点边权 0
#define MAXN 110
void search(int n,int mat[][MAXN],int* dfn,int* low,int now,int& ret,int* key,int& cnt,int
root,int& rd,int* bb){
int i;
dfn[now]=low[now]=++cnt; 78
for (i=0;i
if (!dfn[i]){
search(n,mat,dfn,low,i,ret,key,cnt,root,rd,bb);
if (low[i]
if (low[i]>=dfn[now]){
if (now!=root&&!bb[now])
key[ret++]=now,bb[now]=1;
else if(now==root)
rd++;
}
}
else if (dfn[i]
}
}
int key_vertex(int n,int mat[][MAXN],int* key){
int ret=0,i,cnt,rd,dfn[MAXN],low[MAXN],bb[MAXN];
for (i=0;i
rd=0;
search(n,mat,dfn,low,i,ret,key,cnt,i,rd,bb);
if (rd>1&&!bb[i])
key[ret++]=i,bb[i]=1;
}
return ret;
}
7.2 无向图关键边(dfs邻接阵)
//无向图的关键边,dfs邻接阵形式,O(n^2)
//返回关键边条数,key[][2]返回边集
//传入图的大小 n和邻接阵mat,不相邻点边权 0
#define MAXN 100
void search(int n,int mat[][MAXN],int* dfn,int* low,int now,int& cnt,int key[][2]){
int i;
for (low[now]=dfn[now],i=0;i
if (!dfn[i]){
dfn[i]=dfn[now]+1; 79
search(n,mat,dfn,low,i,cnt,key);
if (low[i]>dfn[now])
key[cnt][0]=i,key[cnt++][1]=now;
if (low[i]
}
else if (dfn[i] low[now]=lev[i];
}
}
int key_edge(int n,int mat[][MAXN],int key[][2]){
int ret=0,i,dfn[MAXN],low[MAXN];
for (i=0;i
dfn[i]=1,bridge(n,mat,dfn,low,i,ret,key);
return ret;
}
7.3 无向图的块(bfs邻接阵)
//无向图的块,dfs邻接阵形式,O(n^2)
//每产生一个块调用dummy
//传入图的大小n和邻接阵mat,不相邻点边权0
#define MAXN 100
#include
void dummy(int n,int* a){
for (int i=0;i
void search(int n,int mat[][MAXN],int* dfn,int* low,int now,int& cnt,int* st,int& sp){
int i,m,a[MAXN];
dfn[st[sp++]=now]=low[now]=++cnt;
for (i=0;i
if (!dfn[i]){
search(n,mat,dfn,low,i,cnt,st,sp);
if (low[i]
if (low[i]>=dfn[now]){ 80
for (st[sp]=-1,a[0]=now,m=1;st[sp]!=i;a[m++]=st[--sp]);
dummy(m,a);
}
}
else if (dfn[i]
}
}
void block(int n,int mat[][MAXN]){
int i,cnt,dfn[MAXN],low[MAXN],st[MAXN],sp=0;
for (i=0;i
search(n,mat,dfn,low,i,cnt,st,sp);
}
7.4 无向图连通分支(dfs/bfs邻接阵)
//无向图连通分支,dfs邻接阵形式,O(n^2)
//返回分支数,id返回1..分支数的值
//传入图的大小 n和邻接阵mat,不相邻点边权0
#define MAXN 100
void floodfill(int n,int mat[][MAXN],int* id,int now,int tag){
int i;
for (id[now]=tag,i=0;i
floodfill(n,mat,id,i,tag);
}
int find_components(int n,int mat[][MAXN],int* id){
int ret,i;
for (i=0;i
floodfill(n,mat,id,i,++ret);
return ret;
}
//无向图连通分支,bfs邻接阵形式,O(n^2)
//返回分支数,id返回 1..分支数的值
//传入图的大小 n和邻接阵mat,不相邻点边权0
#define MAXN 100 81
int find_components(int n,int mat[][MAXN],int* id){
int ret,k,i,j,m;
for (k=0;k
for (id[k]=-1,ret++,m=1;m;)
for (m=i=0;i
for (m++,id[i]=ret,j=0;j
id[j]=-1;
return ret;
}
7.5 有向图强连通分支(dfs/bfs邻接阵)
//有向图强连通分支,dfs邻接阵形式,O(n^2)
//返回分支数,id返回1..分支数的值
//传入图的大小 n和邻接阵mat,不相邻点边权0
#define MAXN 100
void search(int n,int mat[][MAXN],int* dfn,int* low,int now,int& cnt,int& tag,int* id,int* st,int&
sp){
int i,j;
dfn[st[sp++]=now]=low[now]=++cnt;
for (i=0;i
if (!dfn[i]){
ssearch(n,mat,dfn,low,i,cnt,tag,id,st,sp);
if (low[i]
}
else if (dfn[i] for (j=0;j
}
}
if (low[now]==dfn[now])
for (tag++;st[sp]!=now;id[st[--sp]]=tag);
}
int find_components(int n,int mat[][MAXN],int* id){
int ret=0,i,cnt,sp,st[MAXN],dfn[MAXN],low[MAXN]; 82
for (i=0;i
search(n,mat,dfn,low,i,cnt,ret,id,st,sp);
return ret;
}
//有向图强连通分支,bfs邻接阵形式,O(n^2)
//返回分支数,id返回 1..分支数的值
//传入图的大小 n和邻接阵mat,不相邻点边权0
#define MAXN 100
int find_components(int n,int mat[][MAXN],int* id){
int ret=0,a[MAXN],b[MAXN],c[MAXN],d[MAXN],i,j,k,t;
for (k=0;k
for (i=0;i
a[k]=b[k]=1;
for (t=1;t;)
for (t=i=0;i
for (c[i]=t=1,j=0;j
a[j]=1;
if (b[i]&&!d[i])
for (d[i]=t=1,j=0;j
b[j]=1;
}
for (ret++,i=0;i
id[i]=ret;
}
return ret;
}
7.6 有向图最小点基(邻接阵)
//有向图最小点基,邻接阵形式,O(n^2)
//返回电集大小和点集
//传入图的大小 n和邻接阵mat,不相邻点边权0
//需要调用强连通分支
#define MAXN 100 83
int base_vertex(int n,int mat[][MAXN],int* sets){
int ret=0,id[MAXN],v[MAXN],i,j;
j=find_components(n,mat,id);
for (i=0;i
v[id[j]-1]=0;
for (i=0;i
v[id[sets[ret++]=i]-1]=0;
return ret;
}
8、 图论—匹配
8.1 二分图最大匹配(hungary邻接表)
//二分图最大匹配,hungary算法,邻接表形式,复杂度O(m*e)
//返回最大匹配数,传入二分图大小m,n和邻接表 list(只需一边)
//match1,match2返回一个最大匹配,未匹配顶点match值为-1
#include
#define MAXN 310
#define _clr(x) memset(x,0xff,sizeof(int)*MAXN)
struct edge_t{
int from,to;
edge_t* next;
};
int hungary(int m,int n,edge_t* list[],int* match1,int* match2){
int s[MAXN],t[MAXN],p,q,ret=0,i,j,k;edge_t* e;
for (_clr(match1),_clr(match2),i=0;i
for (_clr(t),s[p=q=0]=i;p<=q&&match1[i]<0;p++)
for (e=list[k=s[p]];e&&match1[i]<0;e=e->next)
if (t[j=e->to]<0){
s[++q]=match2[j],t[j]=k;
if (s[q]<0)
for (p=j;p>=0;j=p)
match2[j]=k=t[j],p=match1[k],match1[k]=j;
}
return ret;
} 84
8.2 二分图最大匹配(hungary邻接阵)
//二分图最大匹配,hungary算法,邻接阵形式,复杂度O(m*m*n)
//返回最大匹配数,传入二分图大小m,n和邻接阵 mat,非零元素表示有边
//match1,match2返回一个最大匹配,未匹配顶点match值为-1
#include
#define MAXN 310
#define _clr(x) memset(x,0xff,sizeof(int)*MAXN)
int hungary(int m,int n,int mat[][MAXN],int* match1,int* match2){
int s[MAXN],t[MAXN],p,q,ret=0,i,j,k;
for (_clr(match1),_clr(match2),i=0;i
for (_clr(t),s[p=q=0]=i;p<=q&&match1[i]<0;p++)
for (k=s[p],j=0;j
s[++q]=match2[j],t[j]=k;
if (s[q]<0)
for (p=j;p>=0;j=p)
match2[j]=k=t[j],p=match1[k],match1[k]=j;
}
return ret;
}
8.3 二分图最大匹配(hungary正向表)
//二分图最大匹配,hungary算法,正向表形式,复杂度O(m*e)
//返回最大匹配数,传入二分图大小m,n和正向表 list,buf(只需一边)
//match1,match2返回一个最大匹配,未匹配顶点match值为-1
#include
#define MAXN 310
#define _clr(x) memset(x,0xff,sizeof(int)*MAXN)
int hungary(int m,int n,int* list,int* buf,int* match1,int* match2){
int s[MAXN],t[MAXN],p,q,ret=0,i,j,k,l;
for (_clr(match1),_clr(match2),i=0;i
for (_clr(t),s[p=q=0]=i;p<=q&&match1[i]<0;p++)
for (l=list[k=s[p]];l if (t[j=buf[l]]<0){
s[++q]=match2[j],t[j]=k;
if (s[q]<0)
for (p=j;p>=0;j=p)
match2[j]=k=t[j],p=match1[k],match1[k]=j;
} 85
return ret;
}
8.4二分图最佳匹配(kuhn_munkras 邻接阵)
//二分图最佳匹配,kuhn munkras算法,邻接阵形式,复杂度O(m*m*n)
//返回最佳匹配值,传入二分图大小m,n和邻接阵 mat,表示权值
//match1,match2返回一个最佳匹配,未匹配顶点match值为-1
//一定注意 m<=n,否则循环无法终止
//最小权匹配可将权值取相反数
#include
#define MAXN 310
#define inf 1000000000
#define _clr(x) memset(x,0xff,sizeof(int)*n)
int kuhn_munkras(int m,int n,int mat[][MAXN],int* match1,int* match2){
int s[MAXN],t[MAXN],l1[MAXN],l2[MAXN],p,q,ret=0,i,j,k;
for (i=0;i
for (i=0;i
for (k=s[p],j=0;j
s[++q]=match2[j],t[j]=k;
if (s[q]<0)
for (p=j;p>=0;j=p)
match2[j]=k=t[j],p=match1[k],match1[k]=j;
}
if (match1[i]<0){
for (i--,p=inf,k=0;k<=q;k++)
for (j=0;j
for (j=0;j
}
}
for (i=0;i
return ret;
} 86
8.5 一般图匹配(邻接表)
//一般图最大匹配,邻接表形式,复杂度O(n*e)
//返回匹配顶点对数,match返回匹配,未匹配顶点 match值为-1
//传入图的顶点数n和邻接表 list
#define MAXN 100
struct edge_t{
int from,to;
edge_t* next;
};
int aug(int n,edge_t* list[],int* match,int* v,int now){
int t,ret=0;edge_t* e;
v[now]=1;
for (e=list[now];e;e=e->next)
if (!v[t=e->to]){
if (match[t]<0)
match[now]=t,match[t]=now,ret=1;
else{
v[t]=1;
if (aug(n,list,match,v,match[t]))
match[now]=t,match[t]=now,ret=1;
v[t]=0;
}
if (ret)
break;
}
v[now]=0;
return ret;
}
int graph_match(int n,edge_t* list[],int* match){
int v[MAXN],i,j;
for (i=0;i
for (i=0,j=n;i
if (match[i]<0&&aug(n,list,match,v,i))
i=0,j-=2;
else
i++;
for (i=j=0;i
return j/2;
} 87
8.6 一般图匹配(邻接阵)
//一般图最大匹配,邻接阵形式,复杂度O(n^3)
//返回匹配顶点对数,match返回匹配,未匹配顶点 match值为-1
//传入图的顶点数n和邻接阵 mat
#define MAXN 100
int aug(int n,int mat[][MAXN],int* match,int* v,int now){
int i,ret=0;
v[now]=1;
for (i=0;i
if (match[i]<0)
match[now]=i,match[i]=now,ret=1;
else{
v[i]=1;
if (aug(n,mat,match,v,match[i]))
match[now]=i,match[i]=now,ret=1;
v[i]=0;
}
if (ret)
break;
}
v[now]=0;
return ret;
}
int graph_match(int n,int mat[][MAXN],int* match){
int v[MAXN],i,j;
for (i=0;i
for (i=0,j=n;i
if (match[i]<0&&aug(n,mat,match,v,i))
i=0,j-=2;
else
i++;
for (i=j=0;i
return j/2;
}
8.7 一般图匹配(正向表) 88
//一般图最大匹配,正向表形式,复杂度O(n*e)
//返回匹配顶点对数,match返回匹配,未匹配顶点 match值为-1
//传入图的顶点数n和正向表 list,buf
#define MAXN 100
int aug(int n,int* list,int* buf,int* match,int* v,int now){
int i,t,ret=0;
v[now]=1;
for (i=list[now];i if (!v[t=buf[i]]){
if (match[t]<0)
match[now]=t,match[t]=now,ret=1;
else{
v[t]=1;
if (aug(n,list,buf,match,v,match[t]))
match[now]=t,match[t]=now,ret=1;
v[t]=0;
}
if (ret)
break;
}
v[now]=0;
return ret;
}
int graph_match(int n,int* list,int* buf,int* match){
int v[MAXN],i,j;
for (i=0;i
for (i=0,j=n;i
if (match[i]<0&&aug(n,list,buf,match,v,i))
i=0,j-=2;
else
i++;
for (i=j=0;i
return j/2;
}
9、 图论—网络流
9.1 最大流(邻接阵) 89
//求网络最大流,邻接阵形式
//返回最大流量,flow返回每条边的流量
//传入网络节点数n,容量 mat,源点 source,汇点 sink
#define MAXN 100
#define inf 1000000000
int max_flow(int n,int mat[][MAXN],int source,int sink,int flow[][MAXN]){
int pre[MAXN],que[MAXN],d[MAXN],p,q,t,i,j;
if (source==sink) return inf;
for (i=0;i
for (i=0;i
for (p=q=0;p<=q&&!pre[sink];t=que[p++])
for (i=0;i
pre[que[q++]=i]=t+1,d[i]=d[t]
pre[que[q++]=i]=-t-1,d[i]=d[t]
for (i=sink;i!=source;)
if (pre[i]>0)
flow[pre[i]-1][i]+=d[sink],i=pre[i]-1;
else
flow[i][-pre[i]-1]-=d[sink],i=-pre[i]-1;
}
for (j=i=0;i
}
9.2 上下界最大流(邻接阵)
//求上下界网络最大流,邻接阵形式
//返回最大流量,-1 表示无可行流,flow返回每条边的流量
//传入网络节点数n,容量 mat,流量下界bf,源点source,汇点 sink
//MAXN 应比最大结点数多2,无可行流返回-1时mat 未复原!
#define MAXN 100
#define inf 1000000000
int limit_max_flow(int n,int mat[][MAXN],int bf[][MAXN],int source,int sink,int
flow[][MAXN]){ 90
int i,j,sk,ks;
if (source==sink) return inf;
for (mat[n][n+1]=mat[n+1][n]=mat[n][n]=mat[n+1][n+1]=i=0;i
sk=mat[source][sink],ks=mat[sink][source],mat[source][sink]=mat[sink][source]=inf;
for (i=0;i
for (i=0;i
_max_flow(n,mat,source,sink,flow);
for (i=0;i
for (j=i=0;i
}
9.3 上下界最小流(邻接阵)
//求上下界网络最小流,邻接阵形式
//返回最大流量,-1 表示无可行流,flow返回每条边的流量
//传入网络节点数n,容量 mat,流量下界bf,源点source,汇点 sink
//MAXN 应比最大结点数多2,无可行流返回-1时mat 未复原!
#define MAXN 100
#define inf 1000000000
int limit_min_flow(int n,int mat[][MAXN],int bf[][MAXN],int source,int sink,int
flow[][MAXN]){
int i,j,sk,ks;
if (source==sink) return inf;
for (mat[n][n+1]=mat[n+1][n]=mat[n][n]=mat[n+1][n+1]=i=0;i
sk=mat[source][sink],ks=mat[sink][source],mat[source][sink]=mat[sink][source]=inf;
for (i=0;i
for (i=0;i
_max_flow(n,mat,sink,source,flow); 91
for (i=0;i
for (j=i=0;i
}
9.4 最大流无流量(邻接阵)
//求网络最大流,邻接阵形式
//返回最大流量
//传入网络节点数n,容量 mat,源点 source,汇点 sink
//注意 mat 矩阵被修改
#define MAXN 100
#define inf 1000000000
int max_flow(int n,int mat[][MAXN],int source,int sink){
int v[MAXN],c[MAXN],p[MAXN],ret=0,i,j;
for (;;){
for (i=0;i
for (c[source]=inf;;){
for (j=-1,i=0;i
j=i;
if (j<0) return ret;
if (j==sink) break;
for (v[j]=1,i=0;i
c[i]=mat[j][i]
for (ret+=j=c[i=sink];i!=source;i=p[i])
mat[p[i]][i]-=j,mat[i][p[i]]+=j;
}
}
9.5 最小费用最大流(邻接阵)
//求网络最小费用最大流,邻接阵形式
//返回最大流量,flow返回每条边的流量,netcost返回总费用
//传入网络节点数n,容量 mat,单位费用cost,源点 source,汇点 sink
92
#define MAXN 100
#define inf 1000000000
int min_cost_max_flow(int n,int mat[][MAXN],int cost[][MAXN],int source,int sink,int
flow[][MAXN],int& netcost){
int pre[MAXN],min[MAXN],d[MAXN],i,j,t,tag;
if (source==sink) return inf;
for (i=0;i
for (i=0;i
for (pre[source]=source+1,min[source]=0,d[source]=inf,tag=1;tag;)
for (tag=t=0;t
for (i=0;i
for (netcost+=min[sink]*d[i=sink];i!=source;)
if (pre[i]>0)
flow[pre[i]-1][i]+=d[sink],i=pre[i]-1;
else
flow[i][-pre[i]-1]-=d[sink],i=-pre[i]-1;
}
for (j=i=0;i
}
10、 图论—应用
10.1 欧拉回路(邻接阵)
//求欧拉回路或欧拉路,邻接阵形式,复杂度O(n^2)
//返回路径长度,path返回路径(有向图时得到的是反向路径)
//传入图的大小 n和邻接阵mat,不相邻点边权 0
//可以有自环与重边,分为无向图和有向图
#define MAXN 100
void find_path_u(int n,int mat[][MAXN],int now,int& step,int* path){ 93
int i;
for (i=n-1;i>=0;i--)
while (mat[now][i]){
mat[now][i]--,mat[i][now]--;
find_path_u(n,mat,i,step,path);
}
path[step++]=now;
}
void find_path_d(int n,int mat[][MAXN],int now,int& step,int* path){
int i;
for (i=n-1;i>=0;i--)
while (mat[now][i]){
mat[now][i]--;
find_path_d(n,mat,i,step,path);
}
path[step++]=now;
}
int euclid_path(int n,int mat[][MAXN],int start,int* path){
int ret=0;
find_path_u(n,mat,start,ret,path);
// find_path_d(n,mat,start,ret,path);
return ret;
}
10.2 树的前序表转化
//将用边表示的树转化为前序表示的树
//传入节点数 n和邻接表 list[],邻接表必须是双向的,会在函数中释放
//pre[]返回前序表,map[]返回前序表中的节点到原来节点的映射
#define MAXN 10000
struct node{
int to;
node* next;
};
void prenode(int n,node* list[],int* pre,int* map,int* v,int now,int last,int& id){
node* t;
int p=id++;
for (v[map[p]=now]=1,pre[p]=last;list[now];){
t=list[now],list[now]=t->next;
if (!v[t->to])
prenode(n,list,pre,map,v,t->to,p,id); 94
}
}
void makepre(int n,node* list[],int* pre,int* map){
int v[MAXN],id=0,i;
for (i=0;i
}
10.3 树的优化算法
//最大顶点独立集
int max_node_independent(int n,int* pre,int* set){
int c[MAXN],i,ret=0;
for (i=0;i
for (i=n-1;i>=0;i--)
if (!c[i]){
set[i]=1;
if (pre[i]!=-1)
c[pre[i]]=1;
ret++;
}
return ret;
}
//最大边独立集
int max_edge_independent(int n,int* pre,int* set){
int c[MAXN],i,ret=0;
for (i=0;i
for (i=n-1;i>=0;i--)
if (!c[i]&&pre[i]!=-1&&!c[pre[i]]){
set[i]=1;
c[pre[i]]=1;
ret++;
}
return ret;
}
//最小顶点覆盖集
int min_node_cover(int n,int* pre,int* set){
int c[MAXN],i,ret=0;
for (i=0;i
for (i=n-1;i>=0;i--)
if (!c[i]&&pre[i]!=-1&&!c[pre[i]]){
set[i]=1;
c[pre[i]]=1;
ret++;
}
return ret;
}
//最小顶点支配集
int min_node_dominant(int n,int* pre,int* set){
int c[MAXN],i,ret=0;
for (i=0;i