Cww97 Code Library 2017.07.05
East China Normal University
Chen WeiWen - Software Engineering
Cao ZhiJie - Computer Science
Zhu XuLiang - Mathematics
常用STL
map的Upperbound
map<int,int>::iterator se = mp.upper_bound(mid); 返回迭代器
优先队列
priority_queue<int>Q;//采用默认优先级构造队列 priority_queue<int,vector<int>,cmp1>que1;//最小值优先 priority_queue<int,vector<int>,cmp2>que2;//最大值优先 Q.push(x); int x = Q.top(); Q.pop();
multiset
begin() 返回指向第一个元素的迭代器 clear() 清除所有元素 count() 返回某个值元素的个数 empty() 如果集合为空,返回 true end() 返回指向最后一个元素的迭代器 erase() 删除集合中的元素 ( 参数是一个元素值,或者迭代器) find() 返回一个指向被查找到元素的迭代器 insert() 在集合中插入元素 size() 集合中元素的数目 lower_bound() 返回指向大于(或等于)某值的第一个元素的迭代器 upper_bound() 返回大于某个值元素的迭代器 equal_range() 返回集合中与给定值相等的上下限的两个迭代器 multiset <point> po; multiset <point>::iterator L, R, it;
离散化lower_lound
sort(a + 1, a + A+1); A = unique(a + 1, a + A+1) - (a + 1); for (int i = 1; i <= n; i++){ // segtree int L = lower_bound(a+1, a+A+1, l[i]) - a; int R = lower_bound(a+1, a+A+1, r[i]) - a; T.update(L, R, i, 1, T.M+1,1); } ------------use in ChairTree------------------- for (int i = 1; i <= n; i++) { scanf("%d", &arr[i]); Rank[i] = arr[i]; } sort(Rank + 1, Rank + n+1);//Rank存储原值 int m = unique(Rank + 1, Rank + n +1) - (Rank + 1);//这个m很重要,WA一天系列 for (int i = 1; i <= n; i++) {//离散化后的数组,仅仅用来更新 arr[i] = lower_bound(Rank + 1, Rank + m+1, arr[i]) - Rank; } ==================CZJ排序去重=================== sort(vecs.begin(), vecs.end()); vecs.resize(unique(vecs.begin(), vecs.end()) - vecs.begin());
bitset
构造函数 bitset<n> b; b有n位,每位都为0.参数n可以为一个表达式. 如bitset<5> b0;则"b0"为"00000"; bitset<n> b(unsigned long u); b有n位,并用u赋值;如果u超过n位,则顶端被截除 如:bitset<5>b0(5);则"b0"为"00101"; bitset<n> b(string s); b是string对象s中含有的位串的副本 string bitval ( "10011" ); bitset<5> b0 ( bitval4 ); 则"b0"为"10011"; bitset<n> b(s, pos); b是s中从位置pos开始位的副本,前面的多余位自动填充0; string bitval ("01011010"); bitset<10> b0 ( bitval5, 3 ); 则"b0" 为 "0000011010"; bitset<n> b(s, pos, num); b是s中从位置pos开始的num个位的副本,如果num<n,则前面的空位自动填充0; string bitval ("11110011011"); bitset<6> b0 ( bitval5, 3, 6 ); 则"b0" 为 "100110"; bool any() 是否存在置为1的二进制位?和none()相反 bool none() 是否不存在置为1的二进制位,即全部为0?和any()相反. size_t count() 二进制位为1的个数. size_t size() 二进制位的个数 flip() 把所有二进制位逐位取反 flip(size_t pos)把在pos处的二进制位取反 bool operator[]( size_type _Pos )获取在pos处的二进制位 set() 把所有二进制位都置为1 set(pos) 把在pos处的二进制位置为1 reset() 把所有二进制位都置为0 reset(pos) 把在pos处的二进制位置为0 test(size_t pos)在pos处的二进制位是否为1? unsigned long to_ulong( )用同样的二进制位返回一个unsigned long值 string to_string ()返回对应的字符串.
线性代数
矩阵快速幂
#include<cstdio> #include<cstring> #include<iostream> using namespace std; typedef long long LL; const LL MOD = 1000000007LL; LL n,m; struct matrix{ static const int MATRIX_N = 11; LL a[MATRIX_N][MATRIX_N]; int row, col; matrix():row(MATRIX_N),col(MATRIX_N){memset(a,0,sizeof(a));} matrix(int x, int y):row(x),col(y){memset(a,0,sizeof(a));} LL* operator [] (int x){return a[x];} matrix operator * (matrix x){ matrix tmp(col, x.row); for(int i = 0; i < row; i++) for(int j = 0; j < col; j++) if(a[i][j])//稀疏矩阵优化 for(int k = 0; k < x.col; k++) if (x[j][k]){ tmp[i][k] += a[i][j] * x[j][k]; //mult(a[i][j], x[j][k], MOD); tmp[i][k] %= MOD; } return tmp; } void operator *= (matrix x){*this = *this * x;} matrix operator ^ (LL x){ matrix ret(row, col); for (int i = 0; i < col; i++) ret[i][i] = 1; matrix tmp = *this; for (; x; x >>= 1, tmp *= tmp){if (x&1) ret *= tmp;} return ret; } void print(){ for (int i = 0; i < row; i++){ for (int j = 0; j < col; j++) printf("%lld ",a[i][j]); puts(""); } } }; int main() { LL n; matrix B(3, 1); while(scanf("%lld%lld%lld%lld", &B[0][0], &B[1][0], &B[2][0], &n) == 4) { matrix A(3, 3); A[0][0] = 0; A[0][1] = 1; A[0][2] = 0; A[1][0] = 0; A[1][1] = 0; A[1][2] = 1; A[2][0] = 0; A[2][1] = 1; A[2][2] = 1; A = A ^ n; matrix C(3, 1); C = A * B; printf("%lld\n", (C[0][0] + C[1][0] + C[2][0]) % MOD); } return 0; }
就是快速幂(czj)
LL fast_multi(LL m, LL n, LL mod){//快速乘法 LL ans = 0;//注意初始化是0,不是1 for (;n; n >>= 1){ if (n & 1) ans += m; m = (m + m) % mod;//和快速幂一样,只不过这里是加 m %= mod;//取模,不要超出范围 ans %= mod; } return ans; } LL fast_pow(LL a, LL n, LL mod){//快速幂 LL ans = 1; for (;n;n >>= 1){ if (n & 1) ans = fast_multi(ans, a, mod);//不能直接乘 a = fast_multi(a, a, mod); ans %= mod; a %= mod; } return ans; }
线性递推函数杜教模板
#include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <string> #include <map> #include <set> #include <cassert> using namespace std; #define rep(i,a,n) for (int i=a;i<n;i++) #define per(i,a,n) for (int i=n-1;i>=a;i--) #define pb push_back #define mp make_pair #define all(x) (x).begin(),(x).end() #define fi first #define se second #define SZ(x) ((int)(x).size()) typedef vector<int> VI; typedef long long ll; typedef pair<int,int> PII; const ll mod=1000000007; ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;} // head int _; ll n; namespace linear_seq { const int N=10010; ll res[N],base[N],_c[N],_md[N]; vector<ll> Md; void mul(ll *a,ll *b,ll k) { rep(i,0,k+k) _c[i]=0; rep(i,0,k) if (a[i]) rep(j,0,k) _c[i+j]=(_c[i+j]+a[i]*b[j])%mod; for (int i=k+k-1;i>=k;i--) if (_c[i]) rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod; rep(i,0,k) a[i]=_c[i]; } int solve(ll n,VI a,VI b) { ll ans=0,pnt=0; ll k=SZ(a); assert(SZ(a)==SZ(b)); rep(i,0,k) _md[k-1-i]=-a[i];_md[k]=1; Md.clear(); rep(i,0,k) if (_md[i]!=0) Md.push_back(i); rep(i,0,k) res[i]=base[i]=0; res[0]=1; while ((1ll<<pnt)<=n) pnt++; for (int p=pnt;p>=0;p--) { mul(res,res,k); if ((n>>p)&1) { for (int i=k-1;i>=0;i--) res[i+1]=res[i];res[0]=0; rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod; } } rep(i,0,k) ans=(ans+res[i]*b[i])%mod; if (ans<0) ans+=mod; return ans; } VI BM(VI s) { VI C(1,1),B(1,1); int L=0,m=1,b=1; rep(n,0,SZ(s)) { ll d=0; rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod; if (d==0) ++m; else if (2*L<=n) { VI T=C; ll c=mod-d*powmod(b,mod-2)%mod; while (SZ(C)<SZ(B)+m) C.pb(0); rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod; L=n+1-L; B=T; b=d; m=1; } else { ll c=mod-d*powmod(b,mod-2)%mod; while (SZ(C)<SZ(B)+m) C.pb(0); rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod; ++m; } } return C; } int gao(VI a,ll n) { VI c=BM(a); c.erase(c.begin()); rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod; return solve(n,c,VI(a.begin(),a.begin()+SZ(c))); } }; int main() { for (scanf("%d",&_);_;_--) { scanf("%lld",&n); printf("%d\n",linear_seq::gao(VI{31, 197, 1255, 7997, 50959, 324725, 2069239, 13185773, 84023455},n-2)); } }
高斯消元(naive)
判断是否有解
求解见红书
int Gauss(matrix a, int m, int n){ int x_cnt = 0; int col, k; //col为列号,k为行号 for (k=0,col=0;k<m&&col<n; ++k, ++col){ int r = k; //r为第col列的一个1 for (int i=k;i<m;++i) if (a[i][col])r=i; if (!a[r][col]){ k--; continue;} if (r!=k)for (int i=col;i<=n;++i) swap( a[r][i], a[k][i]); for (int i=k+1;i<m; ++i)if (a[i][col])//消元 for (int j=col;j<=n;++j) a[i][j] ^= a[k][j]; } for (int i=k;i<m;++i) if (a[i][n])return -1; if (k<=n)return n-k; //返回自由元个数 }
NTT(对任意数取模)
// 多项式乘法 系数对MOD=1000000007取模, 常数巨大,慎用 // 只要选的K个素数乘积大于MOD*MOD*N,理论上MOD可以任取。 #define MOD 1000000007 #define K 3 const int m[K] = {1004535809, 998244353, 104857601}; #define G 3 int qpow(int x, int k, int P) { int ret = 1; while(k) { if(k & 1) ret = 1LL * ret * x % P; k >>= 1; x = 1LL * x * x % P; } return ret; } struct _NTT { int wn[25], P; void init(int _P) { P = _P; for(int i = 1; i <= 21; ++i) { int t = 1 << i; wn[i] = qpow(G, (P - 1) / t, P); } } void change(int *y, int len) { for(int i = 1, j = len / 2; i < len - 1; ++i) { if(i < j) swap(y[i], y[j]); int k = len / 2; while(j >= k) { j -= k; k /= 2; } j += k; } } void NTT(int *y, int len, int on) { change(y, len); int id = 0; for(int h = 2; h <= len; h <<= 1) { ++id; for(int j = 0; j < len; j += h) { int w = 1; for(int k = j; k < j + h / 2; ++k) { int u = y[k]; int t = 1LL * y[k+h/2] * w % P; y[k] = u + t; if(y[k] >= P) y[k] -= P; y[k+h/2] = u - t + P; if(y[k+h/2] >= P) y[k+h/2] -= P; w = 1LL * w * wn[id] % P; } } } if(on == -1) { for(int i = 1; i < len / 2; ++i) swap(y[i], y[len-i]); int inv = qpow(len, P - 2, P); for(int i = 0; i < len; ++i) y[i] = 1LL * y[i] * inv % P; } } void mul(int A[], int B[], int len) { NTT(A, len, 1); NTT(B, len, 1); for(int i = 0; i < len; ++i) A[i] = 1LL * A[i] * B[i] % P; NTT(A, len, -1); } }ntt[K]; int tmp[N][K], t1[N], t2[N]; int r[K][K]; int CRT(int a[]) { int x[K]; for(int i = 0; i < K; ++i) { x[i] = a[i]; for(int j = 0; j < i; ++j) { int t = (x[i] - x[j]) % m[i]; if(t < 0) t += m[i]; x[i] = 1LL * t * r[j][i] % m[i]; } } int mul = 1, ret = x[0] % MOD; for(int i = 1; i < K; ++i) { mul = 1LL * mul * m[i-1] % MOD; ret += 1LL * x[i] * mul % MOD; if(ret >= MOD) ret -= MOD; } return ret; } void mul(int A[], int B[], int len) { for(int id = 0; id < K; ++id) { for(int i = 0; i < len; ++i) { t1[i] = A[i]; t2[i] = B[i]; } ntt[id].mul(t1, t2, len); for(int i = 0; i < len; ++i) tmp[i][id] = t1[i]; } for(int i = 0; i < len; ++i) { A[i] = CRT(tmp[i]); } } void init() { for(int i = 0; i < K; ++i) { for(int j = 0; j < i; ++j) { r[j][i] = qpow(m[j], m[i] - 2, m[i]); } } for(int i = 0; i < K; ++i) { ntt[i].init(m[i]); } }
数论
常用公式和找规律
斯特林公式 n 约等于 sqrt(2*pi*n)*pow(1.0*n/e,n) 带标号连通图计数 1 1 1 4 38 728 26704 1866256 251548592 h(n)=2^(n(n-1)/2) f(n) = h(n)-sum{C(n-1,k-1)*f(k)*h(n-k)}(k=1...n-1) 不带标号n个节点的有根树计数 1, 1, 2, 4, 9, 20, 48, 115, 286, 719, 1842, 不带标号n个节点的树的计数 1,2,3,6,11,23,47,106,235 OEIS A(x) = 1 + T(x) - T^2(x)/2 + T(x^2)/2, where T(x) = x + x^2 + 2*x^3 + ... is the g.f. for A000081 错排公式 D[1] = 0; D[2] = 1; for(int i = 3; i < 25; i++) { D[i] = (i - 1) * (D[i - 1] + D[i - 2]); } 卡特兰数 1 2 5 14 42 132 429 1430 4862 16796 binomial(2*n, n)-binomial(2*n, n-1) Sum_{k=0..n-1} a(k)a(n-1-k) Stirling数,又称为斯特灵数。 在组合数学,Stirling数可指两类数,都是由18世纪数学家James Stirling提出的。 第一类Stirling数是有正负的,其绝对值是包含n个元素的集合分作k个环排列的方法数目。 第二类Stirling数是把包含n个元素的集合划分为正好k个非空子集的方法的数目。 递推公式 第一类Stirling数是有正负的,其绝对值是包含n个元素的集合分作k个环排列的方法数目。 递推公式为, S(n,0) = 0, S(1,1) = 1. S(n 1,k) = S(n,k-1) nS(n,k)。 第二类Stirling数是把包含n个元素的集合划分为正好k个非空子集的方法的数目。 递推公式为: S(n,k)=0; (n<k||k=0) S(n,n) = S(n,1) = 1, S(n,k) = S(n-1,k-1) kS(n-1,k). 第一类斯特林数 有符号Stirling数(无符号Stirling数直接取绝对值) n=0 1 n=1 0 1 n=2 0 -1 1 n=3 0 2 -3 1 n=4 0 -6 11 -6 1 n=5 0 24 -50 35 -10 1 n=6 0 -120 274 -225 85 -15 1 n=7 0 720 -1764 1624 -735 175 -21 1 第二类 n=0 1 n=1 0 1 n=2 0 1 1 n=3 0 1 3 1 n=4 0 1 7 6 1 n=5 0 1 15 25 10 1 n=6 0 1 31 90 65 15 1 n=7 0 1 63 301 350 140 21 1 n=8 0 1 127 966 1701 1050 266 28 1 n=9 0 1 255 3025 7770 6951 2646 462 36 1
欧拉筛
#include <cstring> using namespace std; int prime[1100000],primesize,phi[11000000]; bool isprime[11000000]; void getlist(int listsize){ memset(isprime, 1, sizeof(isprime)); isprime[1] = false; for(int i=2;i<=listsize;i++){ if(isprime[i]) prime[++primesize]=i; for(int j = 1; j <= primesize && i*prime[j] <= listsize;j++){ isprime[i*prime[j]] = false; if(i%prime[j] == 0) break; } } }
莫比乌斯函数模板
void Init(){ memset(vis,0,sizeof(vis)); mu[1] = 1; cnt = 0; for(int i=2; i<N; i++){ if(!vis[i]){ prime[cnt++] = i; mu[i] = -1; } for(int j=0; j<cnt&&i*prime[j]<N; j++){ vis[i*prime[j]] = 1; if(i%prime[j]) mu[i*prime[j]] = -mu[i]; else{ mu[i*prime[j]] = 0; break; } } } }
乘法逆元
//扩展欧几里得(扩展gcd) LL ex_gcd(LL a,LL b,LL &x,LL &y){ if (a == 0 && b == 0) return -1; if (b == 0){x = 1; y = 0; return a;} LL d=ex_gcd(b, a % b, y, x); y -= a / b * x; return d; } LL mod_inverse(LL a,LL n){//乘法逆元 LL x,y; LL d = ex_gcd(a,n,x,y); return (x % n + n) % n; } //p是质数可以 快速幂p-2 LL quick_mod(LL a, LL b){ LL ans = 1; a %= MOD; for(;b; b >>= 1, a = a*a % MOD){ if(b & 1) ans = ans * a % MOD; } return ans; } //逆元筛:求1-MAXN的所有关于MOD的逆元 inv[0] = 0; inv[1] = 1; for(i = 2; i < MAXN; i++){ inv[i] = inv[MOD % i] * (MOD - MOD / i) % MOD; f[i] = (f[i-1] * i) % MOD; }
详解ACM组合数处理,
O(n2)算法——杨辉三角
O(n)算法——阶乘取模 + 乘法逆元
C(m,n) = n! / m! / (n - m)!
如果p是质数,直接quick_mod(b, p-2) % p 费马小定理求逆元
LL C(LL n, LL m){ if(m > n) return 0; LL ans = 1; for(int i = 1; i <= m; i++){ LL a = (n + i - m) % MOD; LL b = i % MOD; ans = ans * (a * quick_mod(b, p-2) % MOD) % MOD; } return ans; }
如果n,m很大 达到1e18,但是p很小 <= 1e5 ,我们可以利用这个p
Lucas定理:C(n, m) % p = C(n / p, m / p) * C(n%p, m%p) % p
LL Lucas(LL n, LL m){ if(m == 0) return 1; return C(n % p, m % p) * Lucas(n / p, m / p) % p; } void InitFac(){//阶乘预处理 fac[0] = 1; for(int i=1; i<=n; i++) fac[i] = (fac[i-1] * i) % MOD; } LL C(LL n,LL m,LL p,LL fac[]){ if(n < m) return 0; return fac[n] * quick_mod(fac[m] * fac[n-m], p - 2, p) % p; }
组合数奇偶性结论:
如果(n&m) == m 那么c(m,n)是奇数,否则是偶数
预处理所有数的因数
//预处理所有数的因数表 //SPEED: ECNUOJ 1E6 5000MS O(nlogn) const int N = 100000 + 5; vector<int > factor[N]; void init(){ for(int i = 2; i < N; i ++){ for(int j = i; j < N; j += i){ factor[j].push_back(i); } } } //预处理质因数表 vector<int> x[N]; bool is[N]; void prime() { memset(is, false, sizeof(is)); for (int i=0; i<N; i++) x[i].clear(); for (int j=2; j<N; j+=2) x[j].push_back(2); for (int i=3; i<N; i+=2) if (!is[i]) { for (int j=i; j<N; j+=i) { is[j] = true; x[j].push_back(i); } } }
随机素数测试和大数分解(POJ 1811)
/*************************************************** * Miller_Rabin 算法进行素数测试 * 速度快,可以判断一个 < 2^63 的数是不是素数 ****************************************************/ const int S = 8; //随机算法判定次数,一般8~10就够了 // 快速乘法,计算ret = (a*b)%c a,b,c < 2^63 long long mult_mod(long long a,long long b,long long c) // 快速幂,计算 ret = (a^n)%mod long long pow_mod(long long a,long long n,long long mod) // 通过 a^(n-1)=1(mod n)来判断n是不是素数 // n-1 = x*2^t 中间使用二次判断 // 是合数返回true, 不一定是合数返回false bool check(long long a,long long n,long long x,long long t){ long long ret = pow_mod(a,x,n); long long last = ret; for(int i = 1; i <= t; i++){ ret = mult_mod(ret,ret,n); if(ret == 1 && last != 1 && last != n-1)return true;//合数 last = ret; } if(ret != 1)return true; else return false; } //************************************************** // Miller_Rabin算法 // 是素数返回true,(可能是伪素数) // 不是素数返回false //************************************************** bool Miller_Rabin(long long n){ if (n < 2) return false; if (n == 2) return true; if ((n&1) == 0) return false;//偶数 long long x = n - 1, t = 0; for(; (x&1)==0;){x >>= 1; t++;} srand(time(NULL));/* *************** */ for(int i = 0; i < S; i++){ long long a = rand()%(n-1) + 1; if( check(a,n,x,t) ) return false; } return true; } //********************************************** // pollard_rho 算法进行质因素分解 //********************************************* long long factor[100];//质因素分解结果(刚返回时时无序的) int tol;//质因素的个数,编号0~tol-1 long long gcd(long long a,long long b) //找出一个因子 long long pollard_rho(long long x,long long c){ long long i = 1, k = 2; srand(time(NULL)); long long x0 = rand()%(x-1) + 1; long long y = x0; while(1){ i ++; x0 = (mult_mod(x0,x0,x) + c)%x; long long d = gcd(y - x0,x); if( d != 1 && d != x)return d; if(y == x0) return x; if(i == k){y = x0; k += k;} } } //对 n进行素因子分解,存入factor. k设置为107左右即可 void findfac(long long n,int k){ if(n == 1)return; if(Miller_Rabin(n)){ factor[tol++] = n; return; } long long p = n; int c = k; while( p >= n) p = pollard_rho(p,c--);//值变化,防止死循环k findfac(p,k); findfac(n/p,k); } //POJ 1811 //给出一个N(2 <= N < 2^54),如果是素数,输出"Prime",否则输出最小的素因子 int main(){ int T; scanf("%d",&T); long long n; while(T--){ scanf("%I64d",&n); if(Miller_Rabin(n)) printf("Prime\n"); else{ tol = 0; findfac(n,107); long long ans = factor[0]; for(int i = 1; i < tol; i++) ans = min(ans,factor[i]); printf("%I64d\n",ans); } } return 0; }
中国剩余定理
//可以不满足两两互质 int n; //扩展gcd多了一个变量 void ex_gcd(LL a, LL b, LL &d, LL &x, LL &y){ if (!b) {d = a, x = 1, y = 0;} else{ ex_gcd(b, a % b, d, y, x); y -= x * (a / b); } } //注意M的范围 //如果M是int,都要开ll,M是ll,乘法要用快速乘 LL ex_crt(LL *m, LL *r, int n){ LL M = m[1], R = r[1], x, y, d; for (int i = 2; i <= n; ++i){ ex_gcd(M, m[i], d, x, y); if ((r[i] - R) % d) return -1; x = (r[i] - R) / d * x % (m[i] / d); R += x * M; M = M / d * m[i]; R %= M; } return R > 0 ? R : R + M; }
离散对数(关于方程x^A=B(mod C)的解)
首先判断是否有解,即 a,p 是否互质。不互质即无解。不妨令 x = im − j, 其中
m = ⌈ √ q⌉ , 这样问题变为求得一组 i j 使得条件满足。此时原式变为 a im−j ≡ b (Mod p), 移
项化简得 (a m ) i ≡ ba j (Mod p)。这个时候我们只需穷举 i,j 使得式子成立即可。先从让 j 从
[0,m] 中穷举,并用 hash 记录下 ba j 对应的 j 值。相同的 ba j 记录较大的 j. 接着让 i 从 [1,m]
中穷举,如果 (a m ) i 在 hash 表中有对应的 j 存在,则对应的 im − j 是一组解。其中第一次出
现的为最小的解。
map<LL, int> Hash; LL i, j; LL bsgs(LL a, LL b, LL p){ LL xx, yy; if (exgcd(a, p, xx, yy) != 1)return −1; a %= p; LL m = ceil(sqrt(p)); Hash.clear(); LL tmp, ans = b % p; for (int i = 0; i <= m; ++i){ Hash[ans] = i; ans = ans * a % p; } tmp = f(a, m, p); ans = 1; for (int i = 1; i <= m; ++i){ ans = ans * tmp % p; if (Hash[ans] != 0) return i * m − Hash[ans]; } return −1; }
生成函数 五边形数定理 整数拆分模板
hdu4658
问一个数n能被拆分成多少种方法,且每一种方法里数字重复个数不能超过k(等于k)。
f[n]=∑(-1)^(k-1)(f[n-k(3*k-1)/2]+f[n-k*(3*k+1)/2])
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; const int N = 100005; const int MOD = 1000000007; int dp[N]; void Init() { dp[0] = 1; for(int i=1;i<N;i++){ dp[i] = 0; for(int j=1;;j++){ int t = (3*j-1)*j / 2; if(t > i) break; int tt = dp[i-t]; if(t+j <= i) tt = (tt + dp[i-t-j])%MOD; if(j&1) dp[i] = (dp[i] + tt)%MOD; else dp[i] = (dp[i] - tt + MOD)%MOD; } } } int Work(int n,int k) { int ans = dp[n]; for(int i=1;;i++){ int t = k*i*(3*i-1) / 2; if(t > n) break; int tt = dp[n-t]; if(t + i*k <= n) tt = (tt + dp[n-t-i*k])%MOD; if(i&1) ans = (ans - tt + MOD)%MOD; else ans = (ans + tt)%MOD; } return ans; } int main(){ Init(); int n,k,t; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&k); printf("%d\n",Work(n,k)); } return 0; }
容斥原理dfs
题意找与n,m互质的第k个数
思路:二分
找到最小的x,使得小于或等于x的数中满足条件的数的个数大于或等于k
预处理n,m的质因数表
k是深度,也就是当前质因数位置
t是奇偶判断
s是质数乘积
n是传进去的x
void dfs(LL k,LL t,LL s,LL n) { if(k==num) { if(t&1) ans-=n/s; else ans+=n/s; return; } dfs(k+1,t,s,n); dfs(k+1,t+1,s*fac[k],n); //fac[k]是质因数表 }
//二分调用
dfs(0,0,1,mid);
求(1,b)区间和(1,d)区间里面gcd(x, y) = k的数的对数(1<=x<=b , 1<= y <= d)。
b和d分别除以k之后的区间里面,只需要求gcd(x, y) = 1就可以了,这样子求出的数的对数不变。
这道题目还要求1-3 和 3-1 这种情况算成一种,因此只需要限制x
#include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; #define N 100005 typedef long long ll; vector<int> x[N]; bool is[N]; void prime() { memset(is, false, sizeof(is)); for (int i=0; i<N; i++) x[i].clear(); for (int j=2; j<N; j+=2) x[j].push_back(2); for (int i=3; i<N; i+=2) if (!is[i]) { for (int j=i; j<N; j+=i) { is[j] = true; x[j].push_back(i); } } } int work(int u, int s, int w) { int cnt = 0, v = 1; for (int i=0; i<x[w].size(); i++) { if ((1<<i) & s) { cnt++; v *= x[w][i]; } } int all = u/v; if (cnt % 2 == 0) return -all; else return all; } int main() { prime(); int T, a, b, c, d, k; scanf("%d", &T); for (int cas=1; cas<=T; cas++) { scanf("%d%d%d%d%d", &a, &b, &c, &d, &k); if (k == 0) { printf("Case %d: 0\n", cas); continue; } b /= k, d /= k; if (b > d) { a = b; b = d; d = a; } long long ans = 0; for (int i=1; i<=d; i++) { k = min(i, b); ans += k; for (int j=1; j<(1<<x[i].size()); j++) ans -= work(k, j, i); } printf("Case %d: %I64d\n", cas, ans); } return 0; }
村民排队问题模型
一堆数,其中有一些两两关系,(A,B)表示A在B前面,求排列数
// UVa11174 Stand in a Line // Rujia Liu int mul_mod(int a, int b, int n) { a %= n; b %= n; return (int)((long long)a * b % n); } void gcd(int a, int b, int& d, int& x, int& y) { if(!b){ d = a; x = 1; y = 0; } else{ gcd(b, a%b, d, y, x); y -= x*(a/b); } } int inv(int a, int n) { int d, x, y; gcd(a, n, d, x, y); return d == 1 ? (x%n+n)%n : -1; } #include<cstdio> #include<cstring> #include<vector> using namespace std; const int maxn = 40000 + 10; const int MOD = 1000000007; vector<int> sons[maxn]; int fa[maxn], fac[maxn], ifac[maxn]; int mul_mod(int a, int b) { return mul_mod(a, b, MOD); } // fac[i] = (i!)%MOD, ifac[i]为fac[i]在模MOD下的逆 void preprocess() { fac[0] = ifac[0] = 1; for(int i = 1; i < maxn; i++) { fac[i] = mul_mod(fac[i-1], i); ifac[i] = inv(fac[i], MOD); } } // 组合数C(n,m)除以MOD的余数 int C(int n, int m) { return mul_mod(mul_mod(fac[n], ifac[m]), ifac[n-m]); } // 统计以u为根的子树有多少种排列。size为该子树的结点总数 int count(int u, int& size) { int d = sons[u].size(); vector<int> sonsize; // 各子树的大小 size = 1; int ans = 1; for(int i = 0; i < d; i++) { int sz; ans = mul_mod(ans, count(sons[u][i], sz)); size += sz; sonsize.push_back(sz); } int sz = size-1; // 非根结点的个数 for(int i = 0; i < d; i++) { ans = mul_mod(ans, C(sz, sonsize[i])); sz -= sonsize[i]; } return ans; } int main() { int T; scanf("%d", &T); preprocess(); while(T--) { int n, m; scanf("%d%d", &n, &m); memset(fa, 0, sizeof(fa)); for(int i = 0; i <= n; i++) sons[i].clear(); for(int i = 0; i < m; i++) { int a, b; scanf("%d%d", &a, &b); fa[a] = b; sons[b].push_back(a); } // 没有父亲的结点称为虚拟结点的儿子 for(int i = 1; i <= n; i++) if(!fa[i]) sons[0].push_back(i); int size; printf("%d\n", count(0, size)); } return 0; }
SG函数,博弈
/********************** 每组数据都改变策略 **********************/ #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<algorithm> using namespace std; typedef int LL; const int MAXN = 1e4 + 5; const int MAXM = 1e4; int sg[MAXN]; bool Hash[MAXN]; int f[MAXN]; int N; void getsg(int n){ memset(sg,0,sizeof sg); for (int i=1;i<=MAXM;i++){ memset(Hash,false,sizeof Hash); for(int j = 0; j < N && i >= f[j]; j++) { Hash[sg[i-f[j]]] = true; /****************上海大学校赛教训,板不要理解错。 这不是一堆拆两堆,这是每个可以转移的状态都标记。 ****************/ } for (int j=0;j<=MAXM;j++){ if (!Hash[j]){ sg[i]=j; break; } } //cout << i << " " << sg[i] << endl; } } int main() { //freopen("out.txt", "w", stdout); while(cin >> N, N) { for(int i = 0; i < N; i++) { scanf("%d", f + i); } sort(f, f + N);//一定要排序 getsg(MAXM); int m; cin >> m; for(int i = 0; i < m; i++) { int n; cin >> n; int ans = 0; for(int i = 0; i < n; i++) { int x; scanf("%d", &x); ans ^= sg[x]; } printf("%s", ans ? "W" : "L"); } printf("\n"); } return 0; } /*************** 独立的棋盘横向移动,看成一个子向另一个子一直在减小,NIM两子间距 ***************/ /*********************** 有一个操作可以把一堆拆成两堆,枚举拆分点 ***********************/ for(int j = 0; j <= i - x; j++) { Hash[sg[j] ^ sg[i - x - j]] = 1; } /*********************** 拿走最后一个的人输,需要特判全是1的情况。 全是1,分奇偶。不全是1,同直接NIM ***********************/ /******************** 两维的一样拆分成两个异或。SG[][]两维 0行,0列特殊处理。直接设置成离原点的距离。 2 2 .# .. 2 2 .# .# 0 0 *********************/ /****************** 两个操作,合并两堆,或者取掉1个 ******************/ 最后肯定合并成一堆再一个个取 如果全大于1,先手可以保证NIM胜利的情况下先合并 不全为1,后手可以取完一个一堆的,相当于操作了两次。 此时,DFS+记忆化搜索来解决 dp[i][j]当1的个数为i时,其他全合并起来一共j个 其中的操作包括: 把某堆只有一个的,取走 把两堆只有一个的,合并 把某堆只有一个的,合并给不是一个的 把不是一个的,取走一个 int dfs(int i, int j) { if (dp[i][j] != -1) return dp[i][j]; if (j == 1) return dp[i][j] = dfs(i+1, 0); dp[i][j] = 0; if (i >= 1 && !dfs(i-1, j)) dp[i][j] = 1; else if (j >= 1 && !dfs(i, j-1)) dp[i][j] = 1; else if (i >= 1 && j > 0 && !dfs(i-1, j+1)) dp[i][j] = 1; else if (i >= 2 && ((j >= 1 && !dfs(i-2, j+3)) || (j == 0 && !dfs(i-2, 2)))) dp[i][j] = 1; return dp[i][j]; } /**************** 31 游戏 1~6各4张 ****************/ 直接搜索,据说记忆化也不用
反nim问题
这题与以往的博弈题的胜负条件不同,谁先走完最后一步谁输,但他也是一类Nim游戏,即为anti-nim游戏。
首先给出结论:先手胜当且仅当
①所有堆石子数都为1且游戏的SG值为0(即有偶数个孤单堆-每堆只有1个石子数);
②存在某堆石子数大于1且游戏的SG值不为0.
字符串
Manacher算法(回文串)
#include<cstdio> #include<string> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=233333;//20W //在o(n)时间内算出以每个点为中心的最大回文串长度 int Manacher(string st){ int len = st.size(); int *p = new int[len+1]; memset(p,0,sizeof(p)); int mx = 0,id = 0; for (int i = 1;i <= len; i++){ if (mx > i) p[i] = min(p[2*id-i],mx-i); else p[i] = 1; while (st[i+p[i]] == st[i-p[i]]) p[i]++; if (i + p[i] > mx){mx = i + p[i]; id = i;} } int ma = 0; for (int i = 1; i < len; i++) ma = max(ma, p[i]); delete(p); return ma - 1; } int main(){ //freopen("fuck.in","r",stdin); char st[N]; while (~scanf("%s",st)){ string st0="$#"; for (int i=0; st[i] != '\0'; i++){ st0 += st[i]; st0 += "#"; } printf("%d\n", Manacher(st0)); } return 0; }
KMP(字符串匹配)
#include<cstdio> #include<cstring> using namespace std; typedef long long LL; const int N=100007; const int P=1000000007; char a[N],b[N]; bool mat[N]; int Next[N];//一定要Next,next会CE LL f[N]; void getNext(int m, char b[]){ int i = 0,j = -1; Next[0] = -1; while (i < m){ if (j == -1 || b[i] == b[j]){ if (b[++i] != b[++j]) Next[i]=j; else Next[i] = Next[j]; }else j = Next[j]; } } //主程序里每组数据需要memset a,b数组!!! void KMP(int n,char a[], int m, char b[]){ memset(mat, 0, sizeof(mat)); int i = 0, j = 0; getNext(m, b);//这行视情况可以放在main里面 while (i < n && j < m){ if (j == -1 || a[i] == b[j]) i++, j++; else j = Next[j]; if (!i && !j)break; if (j == m){ mat[i] = 1; //printf("mat[%d]get\n",i); j = Next[j]; } } }
Tire树_刘汝佳
// LA3942 Remember the Word // Rujia Liu #include<cstring> #include<vector> using namespace std; const int maxnode = 4000 * 100 + 10; const int sigma_size = 26; // 字母表为全体小写字母的Trie struct Trie { int ch[maxnode][sigma_size]; int val[maxnode]; int sz; // 结点总数 void clear() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); } // 初始时只有一个根结点 int idx(char c) { return c - 'a'; } // 字符c的编号 // 插入字符串s,附加信息为v。注意v必须非0,因为0代表“本结点不是单词结点” void insert(const 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; // 中间结点的附加信息为0 ch[u][c] = sz++; // 新建结点 } u = ch[u][c]; // 往下走 } val[u] = v; // 字符串的最后一个字符的附加信息为v } // 找字符串s的长度不超过len的前缀 void find_prefixes(const char *s, int len, vector<int>& ans) { int u = 0; for(int i = 0; i < len; i++) { if(s[i] == '\0') break; int c = idx(s[i]); if(!ch[u][c]) break; u = ch[u][c]; if(val[u] != 0) ans.push_back(val[u]); // 找到一个前缀 } } }; #include<cstdio> const int maxl = 300000 + 10; // 文本串最大长度 const int maxw = 4000 + 10; // 单词最大个数 const int maxwl = 100 + 10; // 每个单词最大长度 const int MOD = 20071027; int d[maxl], len[maxw], S; char text[maxl], word[maxwl]; Trie trie; int main() { int kase = 1; while(scanf("%s%d", text, &S) == 2) { trie.clear(); for(int i = 1; i <= S; i++) { scanf("%s", word); len[i] = strlen(word); trie.insert(word, i); } memset(d, 0, sizeof(d)); int L = strlen(text); d[L] = 1; for(int i = L-1; i >= 0; i--) { vector<int> p; trie.find_prefixes(text+i, L-i, p); for(int j = 0; j < p.size(); j++) d[i] = (d[i] + d[i+len[p[j]]]) % MOD; } printf("Case %d: %d\n", kase++, d[0]); } return 0; }
压缩Tire树
// UVa11732 strcmp() Anyone? // Rujia Liu #include<cstring> #include<vector> using namespace std; const int maxnode = 4000 * 1000 + 10; const int sigma_size = 26; // 字母表为全体小写字母的Trie struct Trie { int head[maxnode]; // head[i]为第i个结点的左儿子编号 int next[maxnode]; // next[i]为第i个结点的右兄弟编号 char ch[maxnode]; // ch[i]为第i个结点上的字符 int tot[maxnode]; // tot[i]为第i个结点为根的子树包含的叶结点总数 int sz; // 结点总数 long long ans; // 答案 void clear() { sz = 1; tot[0] = head[0] = next[0] = 0; } // 初始时只有一个根结点 // 插入字符串s(包括最后的'\0'),沿途更新tot void insert(const char *s) { int u = 0, v, n = strlen(s); tot[0]++; for(int i = 0; i <= n; i++) { // 找字符a[i] bool found = false; for(v = head[u]; v != 0; v = next[v]) if(ch[v] == s[i]) { // 找到了 found = true; break; } if(!found) { v = sz++; // 新建结点 tot[v] = 0; ch[v] = s[i]; next[v] = head[u]; head[u] = v; // 插入到链表的首部 head[v] = 0; } u = v; tot[u]++; } } // 统计LCP=u的所有单词两两的比较次数之和 void dfs(int depth, int u) { if(head[u] == 0) // 叶结点 ans += tot[u] * (tot[u] - 1) * depth; else { int sum = 0; for(int v = head[u]; v != 0; v = next[v]) sum += tot[v] * (tot[u] - tot[v]); // 子树v中选一个串,其他子树中再选一个 ans += sum / 2 * (2 * depth + 1); // 除以2是每种选法统计了两次 for(int v = head[u]; v != 0; v = next[v]) dfs(depth+1, v); } } // 统计 long long count() { ans = 0; dfs(0, 0); return ans; } }; #include<cstdio> const int maxl = 1000 + 10; // 每个单词最大长度 int n; char word[maxl]; Trie trie; int main() { int kase = 1; while(scanf("%d", &n) == 1 && n) { trie.clear(); for(int i = 0; i < n; i++) { scanf("%s", word); trie.insert(word); } printf("Case %d: %lld\n", kase++, trie.count()); } return 0; }
数据结构
树状数组(逆序对)
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 1e5 + 7; struct binaryIndexTree{ int val[N], n; inline void init(int n){ this->n = n; memset(val, 0, sizeof(val)); } inline void add(int k, int num){ for (;k <= n; k += k&-k) val[k] += num; } int sum(int k){ int sum = 0; for (; k; k -= k&-k) sum += val[k]; return sum; } int Getsum(LL x1,LL x2){ //求任意区间和 return sum(x2) - sum(x1-1); } } T; int arr[N], n; int main(){ //freopen("in.txt", "r", stdin); for (;~scanf("%d", &n);){ T.init(n); int sum = 0; for (int i = 0; i < n; i++){ scanf("%d", &arr[i]); arr[i]++; sum += T.sum(n) - T.sum(arr[i] - 1); T.add(arr[i], 1); } int ans = sum; for (int i = 0; i < n; i++){ sum += (n - arr[i]) - (arr[i] - 1); ans = min(ans, sum); } printf("%d\n", ans); } }
ZKW线段树(单点修改)
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 2e5 + 7; struct segmentTree{ #define lc (t<<1) #define rc (t<<1^1) int sum[N], M; inline void build(int n){ M = 1; for (;M < n;) M <<= 1; if (M!=1) M--; memset(sum, sizeof(sum), 0); for (int i = 1+M; i <= n+M; i++){ scanf("%d", &sum[i]); } for (int t = M; t >= 1; t--){ sum[t] = sum[lc] + sum[rc]; } } void add(int t, int x){ for (sum[t+=M]+=x, t>>=1; t; t>>=1){ sum[t] = sum[lc] + sum[rc]; } } int query(int l, int r){ int ans = 0; for (l+=M-1,r+=M+1; l^r^1; l>>=1,r>>=1){ if (~l&1) ans += sum[l^1]; if ( r&1) ans += sum[r^1]; } return ans; } } T;
ZKW线段树(RMQ区间操作)
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; const int N = 2e5 + 10; double EPS = 1e-11; const LL INF = 0x3f3f3f3f3f3f3f3f; int n, pos[N], arr[N], pre[N]; struct ZKWsegTree{ double tree[N]; int M, n; void build(int n, double Mid){ this->n = n; M = 1; while (M < n) M <<= 1; if (M!=1) M--; for (int t = 1 ; t <= n; t++) tree[t+M] = 1.0*t*Mid; for (int t = n+1; t <= M+1; t++) tree[t+M] = INF; for (int t = M; t >= 1; t--) tree[t] = min(tree[t<<1], tree[t<<1^1]); for (int t = 2*M+1; t >= 1; t--) tree[t] = tree[t] - tree[t>>1]; } void update(int l, int r, double val){ double tmp; for (l+=M-1, r+=M+1; l^r^1; l>>=1, r>>=1){ if (~l&1) tree[l^1] += val; if ( r&1) tree[r^1] += val; if (l > 1) tmp = min(tree[l], tree[l^1]), tree[l]-=tmp, tree[l^1]-=tmp, tree[l>>1]+=tmp; if (r > 1) tmp = min(tree[r], tree[r^1]), tree[r]-=tmp, tree[r^1]-=tmp, tree[r>>1]+=tmp; } for (; l > 1; l >>= 1){ tmp = min(tree[l], tree[l^1]), tree[l]-=tmp, tree[l^1]-=tmp, tree[l>>1]+=tmp; } tree[1] += tree[0], tree[0] = 0; } double query(int l, int r){ double lAns = 0, rAns = 0; l += M, r += M; if (l != r){ for (; l^r^1; l>>=1, r>>=1){ lAns += tree[l], rAns += tree[r]; if (~l&1) lAns = min(lAns, tree[l^1]); if ( r&1) rAns = min(rAns, tree[r^1]); } } double ans = min(lAns + tree[l], rAns + tree[r]); for (;l > 1;) ans += tree[l>>=1]; return ans; } } T;
常规线段树(区间操作区间和)
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; const LL N = 5e5 + 7; struct segTree{ #define lc (rt<<1) #define rc (rt<<1^1) #define lson l, m, rt<<1 #define rson m+1, r, rt<<1^1 LL M, sum[N], tag[N]; inline void build(LL n){ M = 1; while(M<n) M<<=1; if (M!=1) M--; memset(tag, 0, sizeof(tag)); for (LL leaf = M+1; leaf <= n+M; leaf++) scanf("%lld", &sum[leaf]); for (LL leaf = n+1+M; leaf <= (M<<1^1); leaf++) sum[leaf] = 0; for (LL rt = M; rt >= 1; rt--) sum[rt] = sum[lc] + sum[rc]; } inline void pushUp(LL rt){ sum[rt] = sum[lc] + sum[rc]; } inline void pushDown(LL rt, LL len){ if (tag[rt] == 0) return; tag[lc] += tag[rt]; tag[rc] += tag[rt]; sum[lc] += tag[rt] * (len>>1); sum[rc] += tag[rt] * (len>>1); tag[rt] = 0; } inline void update(LL L, LL R, LL x, LL l, LL r, LL rt){ //printf("update(%d, %d, %d, %d, %d, %d)\n", L, R, x, l, r, rt); if (L <= l && r <= R){ tag[rt] += x; sum[rt] += (r-l+1) * x; return; } pushDown(rt, r-l+1); LL m = (l + r) >> 1; if (L <= m) update(L, R, x, lson); if (m < R) update(L, R, x, rson); pushUp(rt); } LL query(LL L, LL R, LL l, LL r, LL rt){ if (L <= l && r <= R) return sum[rt]; pushDown(rt, r-l+1); LL m = (l + r) >> 1; LL ans = 0; if (L <= m) ans += query(L, R, lson); if (m < R) ans += query(L, R, rson); return ans; } } T;
主席树
poj2104求区间k大值
# include <cstdio> # include <cstring> # include <iostream> # include <algorithm> using namespace std; const int N = 1e5 + 7; int arr[N]; //arr[] 原数组的数在rank[]中的位置; int Rank[N]; //rank[] 原数组离散化 struct ChairTree{ #define sum(x) tree[x].w #define lson tree[rt].lc, tree[rt1].lc, l, m #define rson tree[rt].rc, tree[rt1].rc, m+1, r struct node{ int lc, rc, w; node(){} } tree[N * 20]; int root[N], cnt; void build(){ root[0] = cnt = 0; memset(tree, 0, sizeof(tree)); } void add(int pos, int val, int &rt, int rt1, int l, int r){ tree[rt = ++cnt] = tree[rt1]; tree[rt].w += val; if (l == r) return; int m = (l + r) >> 1; if (pos <= m) add(pos, val, lson); else add(pos, val, rson); } //单点查询 int query(int k, int rt, int rt1, int l, int r){ if (l == r) return l; int lsize = sum(tree[rt1].lc) - sum(tree[rt].lc); int m = (l + r) >> 1; if (lsize >= k) return query(k, lson); else return query(k - lsize, rson); } //区间查询 LL query(int L, int R, int rt, int rt1, int l, int r){ if (L <= l && r <= R) return sum(rt1) - sum(rt); if (sum(rt1) == sum(rt)) return 0; LL ans = 0; int m = (l + r) >> 1; if (L <= m) ans += query(L, R, lson); if (m < R) ans += query(L, R, rson); return ans; } } T; int main(){ //freopen("in.txt","r",stdin); int _, l, r, k, n, q; for (; ~scanf("%d%d", &n, &q);){ T.build(); for (int i = 1; i <= n; i++) { scanf("%d", &arr[i]); Rank[i] = arr[i]; } sort(Rank + 1, Rank + n+1);//Rank存储原值 int m = unique(Rank + 1, Rank + n +1) - (Rank + 1);//这个m很重要,WA一天系列 for (int i = 1; i <= n; i++) {//离散化后的数组,仅仅用来更新 arr[i] = lower_bound(Rank + 1, Rank + m+1, arr[i]) - Rank; } for (int i = 1; i <= n; i++){ T.add(arr[i], 1, T.root[i], T.root[i-1], 1, m);//填m别填n } for (; q--;){ scanf("%d%d%d", &l, &r, &k); int pos = T.query(k, T.root[l-1], T.root[r], 1, m); printf("%d\n", Rank[pos]); } } return 0; }
归并树(区间第K大数)
#include <iostream> #include <algorithm> #include <cstdio> #include <vector> using namespace std; const int ST_SIZE = (1 << 18) - 1; const int MAXN = 1E5 + 5; const int MAXM = 5005; int N, M; int A[MAXN]; // 要处理的数组 int I[MAXM], J[MAXM], K[MAXM]; //存查询 int nums[MAXN]; //对A排序后的数组 vector<int> dat[ST_SIZE]; //线段树节点数组 //构建线段树 // k是节点编号, 和区间[l, r)对应 void init(int k, int l, int r) { if (r - l == 1) { dat[k].push_back(A[l]); } else { int lch = k * 2 + 1, rch = 2 * k + 2; init(lch, l, (l + r) / 2); init(rch, (l + r) / 2, r); dat[k].resize(r - l); //也不知道有什么用,估计是缩小空间吧 //利用STL的merge函数把两个儿子的数列合并 merge(dat[lch].begin(), dat[lch].end(), dat[rch].begin(), dat[rch].end(), dat[k].begin()); } } //计算[i, j)中不超过x的数的个数 //k是节点编号, 和区间[l, r)对应 (一开始 k l r 0 0 N) int query(int i, int j, int x, int k, int l, int r) { if (j <= l || r <= i) return 0;//完全不相交 if (i <= l && r <= j) { return upper_bound(dat[k].begin(), dat[k].end(), x) - dat[k].begin();//完全包含 int lc = query(i, j, x, k * 2 + 1, l, (l + r) / 2); int rc = query(i, j, x, k * 2 + 2, (l + r) / 2, r); return lc + rc; } void solve() { for(int i = 0; i < N; i++) nums[i] = A[i]; sort(nums, nums + N); init(0, 0, N); for (int i = 0; i < M; i++) { //[l, r) int l = I[i] - 1, r = J[i], k = K[i]; int lb = -1, ub = N - 1; //(-1, N-1] while(ub - lb > 1) { int md = (ub + lb) / 2; int ans = query(l, r, nums[md], 0, 0, N); if (ans >= k) ub = md; else lb = md; } printf("%d\n", nums[ub]); } } int main(){ cin >> N >> M; for (int i = 0; i < N; i++) scanf("%d", A + i); for (int i = 0; i < M; i++) { scanf("%d%d%d", I + i, J + i, K + i); } solve(); return 0; }
Treap
#include<cstdio> #include<cstring> #include<cstdlib> #include<cassert> using namespace std; struct Node{ Node *ch[2]; int r, v, s;//s表示节点数 Node(int v):v(v){ ch[0]=ch[1]=NULL; r = rand();//在cstdlib头声明 s = 1; } int cmp(int x){ if (x == v) return -1; return x<v ? 0 : 1; } void maintain(){ s = 1; if(ch[0]!=NULL) s+=ch[0]->s; if(ch[1]!=NULL) s+=ch[1]->s; } }; //root全局使用的话可以在这里跟上*root void rotate(Node* &o,int d){ Node *k=o->ch[d^1]; o->ch[d^1]=k->ch[d]; k->ch[d]=o; o->maintain(); k->maintain(); o=k; } void insert(Node* &o,int x){//o子树中事先不存在x if(o==NULL) o=new Node(x); else{ //如这里改成int d=o->cmp(x); //就不可以插入相同的值,因为d可能为-1 int d=x<(o->v)?0:1; insert(o->ch[d],x); if(o->ch[d]->r > o->r) rotate(o,d^1); } o->maintain(); } void remove(Node* &o,int x){ if (o==NULL) return ;//空时返回 int d=o->cmp(x); if (d == -1){ Node *u=o; if(o->ch[0] && o->ch[1]){ int d2=(o->ch[0]->r < o->ch[1]->r)?0:1; rotate(o,d2); remove(o->ch[d2],x); }else{ if(o->ch[0]==NULL) o=o->ch[1]; else o=o->ch[0]; delete u;//这个要放里面 } } else remove(o->ch[d],x); if(o) o->maintain();//之前o存在,但是删除节点后o可能就是空NULL了,所以需要先判断o是否为空 } //返回关键字从小到大排序时的第k个值 //若返回第K大的值,只需要把ch[0]和ch[1]全互换就可以了 int kth(Node* o,int k){ assert(o && k>=1 && k<=o->s);//保证输入合法,根据实际问题返回 int s=(o->ch[0]==NULL)?0:o->ch[0]->s; if(k==s+1) return o->v; else if(k<=s) return kth(o->ch[0],k); else return kth(o->ch[1],k-s-1); } //返回值x在树中的排名,就算x不在o树中也能返回排名 //返回值范围在[1,o->s+1]范围内 int rank(Node* o,int x){ if(o==NULL) return 1;//未找到x; int num= o->ch[0]==NULL ? 0:o->ch[0]->s; if(x==o->v) return num+1; else if(x < o->v) return rank(o->ch[0],x); else return rank(o->ch[1],x)+num+1; } int main(){ int n=0, v; while(scanf("%d",&n)==1 && n){ Node *root=NULL; //初始化为NULL for(int i=0; i<n; i++){ int x; scanf("%d",&x); if(root==NULL) root=new Node(x); else insert(root,x); } while(scanf("%d",&v)==1){ printf("%d\n",rank(root,v)); } } return 0; }
分块
分块入门 1
给出一个长为n的数列,以及n个操作,操作涉及区间加法,单点查值。
int n, blo; int v[50005],bl[50005],atag[50005]; void add(int a, int b, int c){ for(int i=a;i<=min(bl[a]*blo,b);i++) v[i]+=c; if(bl[a]!=bl[b]) for(int i=(bl[b]-1)*blo+1;i<=b;i++) v[i]+=c; for(int i=bl[a]+1;i<=bl[b]-1;i++) atag[i]+=c; } int main(){ n=read(); blo=sqrt(n); for(int i=1;i<=n;i++)v[i]=read(); for(int i=1;i<=n;i++)bl[i]=(i-1)/blo+1; for(int i=1;i<=n;i++){ int f=read(),a=read(),b=read(),c=read(); if(f==0)add(a,b,c); if(f==1)printf("%d\n",v[b]+atag[bl[b]]); } return 0; }
分块入门 2
给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的元素个数。
int n,blo; int v[50005], bl[50005], atag[50005]; vector<int>ve[505]; void reset(int x){ ve[x].clear(); for(int i=(x-1)*blo+1;i<=min(x*blo,n);i++) ve[x].push_back(v[i]); sort(ve[x].begin(),ve[x].end()); } void add(int a,int b,int c){ for(int i=a;i<=min(bl[a]*blo,b);i++) v[i]+=c; reset(bl[a]); if(bl[a]!=bl[b]){ for(int i=(bl[b]-1)*blo+1;i<=b;i++)v[i]+=c; reset(bl[b]); } for(int i=bl[a]+1;i<=bl[b]-1;i++) atag[i]+=c; } int query(int a,int b,int c){ int ans=0; for(int i=a;i<=min(bl[a]*blo,b);i++) if(v[i]+atag[bl[a]]<c)ans++; if(bl[a]!=bl[b]) for(int i=(bl[b]-1)*blo+1;i<=b;i++) if(v[i]+atag[bl[b]]<c)ans++; for(int i=bl[a]+1;i<=bl[b]-1;i++){ int x=c-atag[i]; ans+=lower_bound(ve[i].begin(),ve[i].end(),x)-ve[i].begin(); } return ans; } int main(){ n=read();blo=sqrt(n); for(int i=1;i<=n;i++)v[i]=read(); for(int i=1;i<=n;i++){ bl[i]=(i-1)/blo+1; ve[bl[i]].push_back(v[i]); } for(int i=1;i<=bl[n];i++) sort(ve[i].begin(),ve[i].end()); for(int i=1;i<=n;i++){ int f=read(),a=read(),b=read(),c=read(); if(f==0)add(a,b,c); if(f==1)printf("%d\n",query(a,b,c*c)); } return 0; }
分块入门 3
给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的前驱(比其小的最大元素)。
int n,blo; int v[100005],bl[100005],atag[100005]; set<int>st[105]; void add(int a,int b,int c){ for(int i=a;i<=min(bl[a]*blo,b);i++){ st[bl[a]].erase(v[i]); v[i] += c; st[bl[a]].insert(v[i]); } if(bl[a]!=bl[b]){ for(int i=(bl[b]-1)*blo+1;i<=b;i++){ st[bl[b]].erase(v[i]); v[i] += c; st[bl[b]].insert(v[i]); } } for(int i=bl[a]+1;i<=bl[b]-1;i++) atag[i]+=c; } int query(int a,int b,int c){ int ans=-1; for(int i=a;i<=min(bl[a]*blo,b);i++){ int val=v[i]+atag[bl[a]]; if(val<c)ans=max(val,ans); } if(bl[a]!=bl[b]) for(int i=(bl[b]-1)*blo+1;i<=b;i++){ int val=v[i]+atag[bl[b]]; if(val<c)ans=max(val,ans); } for(int i=bl[a]+1;i<=bl[b]-1;i++){ int x=c-atag[i]; set<int>::iterator it=st[i].lower_bound(x); if(it==st[i].begin())continue; --it; ans=max(ans,*it+atag[i]); } return ans; } int main(){ n=read();blo=1000; for(int i=1;i<=n;i++)v[i]=read(); for(int i=1;i<=n;i++){ bl[i]=(i-1)/blo+1; st[bl[i]].insert(v[i]); } for(int i=1;i<=n;i++){ int f=read(),a=read(),b=read(),c=read(); if(f==0)add(a,b,c); if(f==1)printf("%d\n",query(a,b,c)); } return 0; }
左偏树
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; const int MAXN = 1e5 + 5; struct node{ int l,r,dis,key; } tree[MAXN]; int far[MAXN]; int Find(int x) { if(far[x] == x) return x; return far[x] = Find(far[x]); } int merge(int a,int b){ if(!a) return b; if(!b) return a; if(tree[a].key < tree[b].key) swap(a, b);//大堆 tree[a].r = merge(tree[a].r,b); far[tree[a].r] = a;//并查 if(tree[tree[a].l].dis < tree[tree[a].r].dis) swap(tree[a].l,tree[a].r); if(tree[a].r)tree[a].dis = tree[tree[a].r].dis + 1; else tree[a].dis = 0; return a; } int pop(int a){ int l = tree[a].l; int r = tree[a].r; far[l] = l;//因为要暂时删掉根,所以左右子树先作为根 far[r] = r; tree[a].l = tree[a].r = tree[a].dis = 0; return merge(l,r); } int main(){ int N, M; while(cin >> N){ for(int i = 1; i <= N; i++){ int x; far[i] = i; scanf("%d", &x); tree[i].key = x; tree[i].l = tree[i].r = tree[i].dis = 0; } cin >> M; while(M--) { int x, y; scanf("%d%d", &x, &y); x = Find(x); y = Find(y); if(x == y) { printf("-1\n"); } else { int ra = pop(x); tree[x].key /= 2; ra = merge(ra, x); int rb = pop(y); tree[y].key /= 2; rb = merge(rb, y); x = merge(ra, rb); printf("%d\n", tree[x].key); } } } return 0; }
Splay
#include<cstdio> #include<algorithm> using namespace std; struct Node{ int key;//size Node *l,*r,*f;//left,right,father }; class SplayTree{ public: void Init(){rt=NULL;} void Zag(Node *x){//left rotate Node *y=x->f;//y is the father of x y->r = x->l; if (x->l)x->l->f = y;//if x has left child x->f =y->f; if (y->f){//y is not root if (y==y->f->l)y->f->l=x;//y if left child else y->f->r=x;//y is right child } y->f=x; x->l=y; } void Zig(Node *x){//right rotate Node *y=x->f;//y is the father of x y->l = x->r; if (x->r)x->r->f=y; x->f = y->f; if (y->f){ if (y==y->f->l)y->f->l=x; else y->f->r=x; } y->f=x; x->r=y; } void Splay(Node *x){ while (x->f){ Node *p=x->f; if (!p->f){ if (x==p->l)Zig(x); else Zag(x); }else if (x==p->l){ if (p==p->f->l){Zig(p);Zig(x);} else {Zig(x);Zag(x);} }else {//x==p->r if (p==p->f->r){Zag(p);Zag(x);} else {Zag(x);Zig(x);} } } rt=x; } Node *Find(int x){ Node *T=rt; while (T){ if (T->key==x){Splay(T);return T;} else if (x<T->key)T=T->l; else T=T->r; } return T; } void Insert(int x){ Node *T=rt,*fa=NULL; while (T){ fa=T; if (x<T->key)T=T->l; else if(x>T->key)T=T->r; else return ;//two the same keys } T=(Node*)malloc(sizeof(Node)); T->key=x; T->l=T->r=NULL; T->f=fa; if (fa){ if (fa->key>x)fa->l=T; else fa->r=T; } Splay(T); } void Delete(int x){ Node *T=Find(x); if (NULL==T)return ;//error rt=Join(T->l,T->r); } Node *Maxnum(Node *t){ Node *T=t; while (T->r)T=T->r; Splay(T); return T; } Node *Minnum(Node *t){ Node *T=t; while (T->l)T=T->l; Splay(T); return T; } Node *Last(int x){ Node *T=Find(x); T=T->l; return (Maxnum(T)); } Node *Next(int x){ Node *T=Find(x); T=T->r; return (Minnum(T)); } Node *Join(Node *t1,Node *t2){ if (NULL==t1)return t2; if (NULL==t2)return t1; Node *T=Maxnum(t1); T->l=t2; return T; } void Split(int x,Node *&t1,Node *&t2){ Node *T=Find(x); t1=T->l; t2=T->r; } void Inorder(Node *T){ if (NULL==T)return ; Inorder(T->l); printf("%d->",T->key); Inorder(T->r); } void _Delete(){Delete(rt);} void Delete(Node *T){ if (NULL==T)return ; Delete(T->l); Delete(T->r); free(T); } private: Node *rt;//root };
AVL树
//codevs1285 莯ʕѸ˹ //by cww97 #include<cstdio> #include<iostream> #include<algorithm> #define INF 0xfffffff #define BASE 1000000 using namespace std; int ans=0; struct Node{ int x,bf,h;//bf=balance factor,h=height Node *l,*r; }; class AVLTree{ public: void Init() { rt = NULL; } int H(Node *T){return (T==NULL)?0:T->h;} int BF(Node *l,Node *r){//get balance factor if (NULL==l && NULL==r) return 0; else if (NULL == l) return -r->h; else if (NULL == r) return l->h; return l->h - r->h; } Node *Lrorate(Node *a){//left rorate Node *b; b=a->r; a->r=b->l; b->l=a; a->h=max(H(a->l),H(a->r)) + 1; b->h=max(H(b->l),H(b->r)) + 1; a->bf=BF(a->l,a->r); b->bf=BF(b->l,b->r); return b; } Node *Rrorate(Node *a){//right rorate Node *b; b=a->l; a->l=b->r; b->r=a; a->h=max(H(a->l),H(a->r)) + 1; b->h=max(H(b->l),H(b->r)) + 1; a->bf=BF(a->l,a->r); b->bf=BF(b->l,b->r); return b; } Node *LRrorate(Node *a){//left then right a->l = Lrorate(a->l); Node *c; c=Rrorate(a); return c; } Node *RLrorate(Node *a){//right then left a->r=Rrorate(a->r); Node *c; c=Lrorate(a); return c; } void Insert(int x){_Insert(rt,x);} void _Insert (Node *&T,int x){ if (NULL==T){ T=(Node*)malloc(sizeof(Node)); T->x=x; T->bf=0;T->h=1; T->l=T->r=NULL; return ; } if (x < T->x) _Insert(T->l,x); else if (x > T->x) _Insert(T->r,x); else return ; //error :the same y T->h=max(H(T->l),H(T->r))+1;//maintain T->bf=BF(T->l,T->r); if (T->bf > 1 || T->bf < -1){//not balanced if (T->bf > 0 && T->l->bf > 0)T=Rrorate(T); else if (T->bf < 0 && T->r->bf < 0)T=Lrorate(T); else if (T->bf > 0 && T->l->bf < 0)T=LRrorate(T); else if (T->bf < 0 && T->r->bf > 0)T=RLrorate(T); } } void GetPet(int x){//get pet or person if (NULL==rt){return ;} int small=0,large=INF; //printf("x=%d\n",x); int flag; if (Find(rt,x,small,large)){ printf("find %d\n",x); _Delete(rt,x); }else if (small==0)flag=1; else if (large==INF)flag=0; else if (large-x<x-small)flag=1; else flag=0; if (!flag){//choose large _Delete(rt,small); ans=(ans+x-small)%BASE; }else { _Delete(rt,large); ans=(ans+large-x)%BASE; } } bool Find(Node *T,int x,int &small,int &large){ if (NULL==T)return 0; if (x==T->x)return 1; if (x<T->x){ large=min(large,T->x); return Find(T->l,x,small,large); }else{ small=max(small,T->x); return Find(T->r,x,small,large); } } void _Delete(Node *&T,int x){ if (NULL==T)return ; if (x < T->x){//y at left _Delete(T->l,x); T->bf=BF(T->l,T->r); if (T->bf<-1){ if (1==T->r->bf)T=RLrorate(T); else T=Lrorate(T);//bf==0 or -1 } }else if (x > T->x){//y at right _Delete(T->r,x); T->bf=BF(T->l,T->r); if (T->bf>1){ if (-1==T->l->bf)T=LRrorate(T); else T=Rrorate(T);//bf==0 or 1 } }else {//here is x if (T->l&&T->r){//left &&right Node *t=T->l; while (t->r)t=t->r; T->x=t->x; _Delete(T->l,t->x); T->bf=BF(T->l,T->r); if (T->bf<-1){ if (1==T->r->bf)T=RLrorate(T); else T=Lrorate(T);//bf==0 or -1 } }else {//left || right Node *t=T; if (T->l)T=T->l; else if(T->r)T=T->r; else {free(T);T=NULL;} if (T)free(t); } } } //Debug,you will not need it at this problem void show(){InOrder(rt);puts("EndShow");} void InOrder(Node *T){//print l rt r if (NULL==T)return ; InOrder(T->l); printf("%d ",T->x); InOrder(T->r); } void Free(){FreeTree(rt);} void FreeTree(Node *T){ if (NULL==T)return ; FreeTree(T->l); FreeTree(T->r); free(T); } private: Node *rt;//root }; int main(){ freopen("fuck.in","r",stdin); int n,x,op,a=0,b=0; scanf("%d",&n); AVLTree T; T.Init(); for (;n--;){ scanf("%d%d",&op,&x); //if pets>people put pets into the tree //else put people into the tree if (op==0){//come a pet a++; if (a>b)T.Insert(x);//more pet else T.GetPet(x);//more people }else{//come a person b++; if (a<b)T.Insert(x);//more people else T.GetPet(x);//more pet } } printf("%d\n",ans%BASE); T.Free(); return 0; }
图论
最小生成树(prim)
hdu1102
#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int N=107; int n,g[N][N]; int prim(){ int minw[N];//MinWeight bool used[N]; memset(used,0,sizeof(used)); memset(minw,0x7f,sizeof(minw)); minw[1]=0; int sum=0; while (1){ int v=-1; for (int i=1;i<=n;i++){ if (!used[i]&&(v==-1||minw[i]<minw[v]))v=i; } if (v==-1)break; used[v]=1; sum+=minw[v]; for (int i=0;i<=n;i++){ minw[i]=min(minw[i],g[v][i]); } } return sum; } int main(){ for (;scanf("%d",&n)==1;){ for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) scanf("%d",&g[i][j]); int x,y,q; scanf("%d",&q); for (;q--;){ scanf("%d%d",&x,&y); g[x][y]=g[y][x]=0; } printf("%d\n",prim()); } return 0; }
次小生成树
#include<iostream> #include<cstdio> #include<cstring> #include<climits> #include<algorithm> using namespace std; #define N 510 int map[N][N], lowcost[N], pre[N], max1[N][N], stack[N]; bool visit[N]; int n, m, sum; void prim(){ //默认1在MST中 int temp, k; int top; //保存最小生成树的结点 memset(visit, false, sizeof(visit)); //初始化 visit[1] = true; sum = top = 0; for(int i = 1; i <= n; ++i){ pre[i] = 1; lowcost[i] = map[1][i]; } lowcost[1] = 0; stack[top++] = 1; //保存MST的结点 for(int i = 1; i <= n; ++i){ temp = INT_MAX; for(int j = 1; j <= n; ++j) if(!visit[j] && temp > lowcost[j]) temp = lowcost[k = j]; if(temp == INT_MAX) break; visit[k] = true; sum += temp; for(int j = 0; j < top; ++j) //新加入点到MST各点路径最大值 max1[stack[j]][k] = max1[k][stack[j]] = max(max1[stack[j]][pre[k]], temp); stack[top++] = k; //保存MST的结点 for(int j = 1; j <= n; ++j) //更新 if(!visit[j] && lowcost[j] > map[k][j]){ lowcost[j] = map[k][j]; pre[j] = k; //记录直接前驱 } } } int main(){ int ncase, start, end, cost, minn; scanf("%d", &ncase); while(ncase--) { for(int i = 1; i < N; ++i) //初始化不为0,1必须用循环。。。。 for(int j = 1; j < N; ++j){ map[i][j] = INT_MAX; max1[i][j] = 0; } scanf("%d%d", &n, &m); for(int i = 1; i <= m; ++i){ scanf("%d%d%d", &start, &end, &cost); //if(cost < map[start][end])(POJ竟然出现重边的时候不选择最小的~~~) map[start][end] = map[end][start] = cost; } prim(); minn = INT_MAX; for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j) if(i != j && i != pre[j] && j != pre[i]) //枚举MST以外的边 minn = min(minn, map[i][j] - max1[i][j]); //求出{MST外加入边-MST环上权值最大边}最小值 if(minn != 0) printf("No\n"); else printf("Yes\n"); } return 0; }
最短路(SPFA)
#include <queue> #include <cstdio> #include <vector> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 100007; const int INF=0x3f3f3f3f; struct SPFA{ struct Edge{ int from,to,cost,a,b; Edge(){} Edge(int x,int y,int z,int a,int b): from(x),to(y),cost(z),a(a),b(b){} bool canPass(){return a>=cost;}//题目里的 void read(){scanf("%d%d%d%d%d",&from,&to,&a,&b,&cost);} }; int n, m; vector <Edge> edges; vector <int > G[N]; inline void AddEdge(){ Edge e; e.read(); if (!e.canPass())return ; edges.push_back(e); int top = edges.size(); G[e.from].push_back(top-1); } inline void Init(int n,int m){ this -> n = n; this -> m = m; edges.clear(); for (int i=0;i<=n;i++)G[i].clear(); for (int i=1;i<=m;i++) AddEdge(); } bool inq[N]; int d[N],cnt[N]; inline int spfa(int s,int t){ queue<int> Q; memset(inq, 0, sizeof(inq)); memset(cnt, 0, sizeof(cnt)); memset( d ,INF,sizeof( d )); d[s] = 0;inq[s]=1;Q.push(s); for (;!Q.empty();){ int u = Q.front();Q.pop(); inq[u]= 0; for (int i=0;i<G[u].size();i++){ Edge &e = edges[G[u][i]]; int val = d[u],s = d[u] ;// val %= (e.a + e.b);//特殊题目 if (val>e.a) s+=e.b-(val-e.a);// else if (e.a<val+e.cost)s+=e.a+e.b-val;// if (d[u]<INF&&s+e.cost<d[e.to]){ d[e.to] = s + e.cost; if (!inq[e.to]){ Q.push(e.to);inq[e.to] = 1; if (++cnt[e.to]>n)return INF; } } } } return d[t]; } }g; int main(){ //freopen("in.txt","r",stdin); int n,m,s,t; for (int T=0;~scanf("%d%d%d%d",&n,&m,&s,&t);){ g.Init(n,m,s,t); int ans = g.spfa(s,t); printf("Case %d: %d\n",++T,ans); } return 0; }
找负环
bool find_negative_loop() { memset(d, 0, sizeof(d)); for(int i = 0; i < V; i++) { for(int j = 0; j < E; j++) { edge e = es[j]; if(d[e.to] > d[e.form] + e.cost) { d[e.to] = d[e.from] + e.cost; if(i == V - 1) return true; } } } return false; }
最短路Dijkstra同时求解次短路
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> using namespace std; typedef long long LL; typedef pair<LL, int> P; const int INF = 0x3f3f3f3f3f3f3f3f; const int N = 2e5 + 7; struct Edge{ int to; LL cost; Edge(int tv = 0, LL tc = 0):to(tv), cost(tc){} }; vector<Edge> G[N]; int n, m; LL dist[N]; //最短距离 LL dist2[N]; //次短距离 LL Dijkstra(){ memset(dist, INF, sizeof(dist)); memset(dist2, INF, sizeof(dist2)); //从小到大的优先队列 //使用pair而不用edge结构体 //是因为这样我们不需要重载运算符 //pair是以first为主关键字进行排序 priority_queue<P, vector<P>, greater<P> > Q; //初始化源点信息 dist[1] = 0; Q.push(P(0, 1)); //同时求解最短路和次短路 for(; !Q.empty();){ P p = Q.top(); Q.pop(); //first为s->to的距离,second为edge结构体的to int v = p.second; LL d = p.first; //当取出的值不是当前最短距离或次短距离,就舍弃他 if (dist2[v] < d) continue; for (int i = 0; i < G[v].size(); i++){ Edge &e = G[v][i]; LL d2 = d + e.cost; if (dist[e.to] > d2){ swap(dist[e.to], d2); Q.push(P(dist[e.to], e.to)); } //printf("dist2[%d] = %d, d2 = %d\n", e.to, dist2[e.to], d2); if (dist2[e.to] > d2 && dist[v] < d2){ dist2[e.to] = d2; Q.push(P(dist2[e.to], e.to)); } } } return dist2[n]; }
多源最短路(Floyed)
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int INF = 0x3f3f3f3f; const int N = 200; int n, G[N][N], d[N][N]; inline void Init(int n){ this -> n = n; memset(G,INF,sizeof(G)); memset(d,INF,sizeof(d)); } inline void AddEdge(int f,int t){ G[f][t] = d[f][t] = 1; } inline void floyed(){ for (int k=1;k<=n;k++) for (int i=1;i<=n;i++)if (i!=k) for (int j=1;j<=n;j++)if (j!=i&&j!=k) d[i][j]=min(d[i][j], d[i][k]+d[k][j]); }
欧拉回路dfs
#include<stack> #include<cstdio> #include<cstring> #include<iostream> using namespace std; typedef long long LL; const int N = 9999,INF=0x3f3f3f3f; struct EulerCircle{ struct Edge{ int to,nxt; Edge(){} Edge(int x,int y):to(x),nxt(y){} }edges[N]; int head[N],n,E; inline void Init(const int &n){ this->n = n; E = 0; for (int i=0;i<=n;i++)head[i]=-1; } inline void AddEdge(int f,int t){ edges[++E] = Edge(t,head[f]); head[f] = E ; } stack<int >S; bool vis[N];//use when dfs inline void dfs(int x){//get EulerCircle Edge e; for (int i=head[x];i!=-1;i=e.nxt){ e = edges[i]; if (vis[i])continue; vis[i] = 1; dfs(e.to); S.push(x); } } inline void getEulerCircle(){ while (!S.empty())S.pop(); memset(vis,0,sizeof(vis)); dfs(1); for (;!S.empty();S.pop()) printf("%d ",S.top()); puts("1"); } } g ;
混合图欧拉回路
解析见紫书376
ATTENTION:需要注意的是,网络流里是有反向边的,dinic跑完之后反向边不要添加到新图里面了
加到Dinic里面
inline void buildEuler(int n){ for (int i=1;i<=n;i++){ for (int nxt,j=head[i];j!=-1;j=nxt){ Edge &e = edges[j]; nxt = e.nxt; if (e.to==s||e.to==t) continue ; if (!e.cap)continue; if (e.flow==e.cap)gg.AddEdge(e.to,e.from); else gg.AddEdge(e.from, e.to); } } }
main哇哦
int d[N];//degree = out - in bool work(int n){ int flow = 0; for (int i=1;i<=n;i++){ if (d[1]&1)return 0; if (d[i]>0){ g.AddEdge(g.s,i,d[i]>>1); flow += d[i]>>1; }else if (d[i]<0) g.AddEdge(i,g.t,-(d[i]>>1)); } if (flow != g.maxFlow()) return 0; return 1; } int main(){ //freopen("in.txt","r",stdin); int T,x,y,n,m; scanf("%d",&T); for (char ch;T--;){ scanf("%d%d",&n,&m); g.Init(n,0,n+1); gg.Init(n); memset(d,0,sizeof(d)); for (int i=1;i<=m;i++){ scanf("%d%d %c\n",&x,&y,&ch); if (ch=='D') gg.AddEdge(x,y); else g.AddEdge(x,y,1); d[x]++;d[y]--;//Degree } if (!work(n))puts("No euler circuit exist"); else { g.buildEuler(n); gg.getEulerCircle(); } if (T)puts(""); } return 0; }
拓扑排序topsort
#include <iostream> #include <cstring> #include <queue> #include <cstdio> using namespace std; const int maxn=107; int x,y,m,n; bool f[maxn][maxn]; int in[maxn]; int main(){ //freopen("fuck.in" ,"r",stdin); while(scanf("%d%d",&n,&m)==2&&(m||n)){ memset(f ,0,sizeof(f )); memset(in,0,sizeof(in)); for(;m--;){ scanf("%d%d",&x,&y); f[x][y] = 1; in[y]++; } int A=0,ans[maxn]; queue<int>Q; for (int i=1;i<=n;i++)if (in[i]==0)Q.push(i); while(!Q.empty()){ int x=Q.front(); Q.pop(); ans[++A]=x; for(int j=1;j<=n;j++)if (f[x][j]){ if (--in[j]==0)Q.push(j); } } for (int i=1;i<A;i++)printf("%d ",ans[i]); printf("%d\n",ans[A]); } return 0; }
最大流(Dinic)
#include<queue> #include<stack> #include<cstdio> #include<vector> #include<cstring> #include<iostream> using namespace std; typedef long long LL; const int INF=0x3f3f3f3f; const int N = 9999; struct Dinic{ struct Edge{ int from,to,cap,flow,nxt; Edge(){} Edge(int u,int v,int c,int f,int n): from(u),to(v),cap(c),flow(f),nxt(n){} }edges[N]; int n, s, t, E, head[N]; bool vis[N]; //use when bfs int d[N],cur[N];//dist,now edge,use in dfs inline void AddEdge(int f,int t,int c){ edges[++E] = Edge(f,t,c,0,head[f]); head[f] = E; edges[++E] = Edge(t,f,0,0,head[t]); head[t] = E; } inline void Init(int n,int s,int t){ this -> n = n ; E = -1; this -> s = s ; head[s] = -1; this -> t = t ; head[t] = -1; for (int i=0;i<=n;i++) head[i] = -1; } inline bool BFS(){ memset(vis,0,sizeof(vis)); queue<int >Q; d[s] = 0; vis[s] = 1; for (Q.push(s);!Q.empty();){ int x = Q.front(); Q.pop(); for (int nxt,i = head[x];i!=-1;i = nxt){ Edge &e = edges[i]; nxt = e.nxt; if (vis[e.to]||e.cap<=e.flow)continue; vis[e.to]=1; d[e.to]=d[x]+1; Q.push(e.to); } } return vis[t]; } inline int DFS(const int& x,int a){ if (x==t||a==0){return a;} int flow = 0, f, nxt; for (int& i=cur[x];i!=-1;i=nxt){ Edge& e = edges[i]; nxt = e.nxt; if (d[x]+1!=d[e.to])continue; if ((f=DFS(e.to,min(a,e.cap-e.flow)))<=0)continue; e.flow += f; edges[i^1].flow-=f;//ϲߍ flow+=f; a-=f; if (!a) break; } return flow; } inline int maxFlow(){return maxFlow(s,t);} inline int maxFlow(int s, int t){ int flow = 0; for (;BFS();){ for (int i=0;i<=n;i++)cur[i]=head[i]; flow += DFS(s,INF) ; } return flow; } inline void buildEuler(int n){ for (int i=1;i<=n;i++){ for (int nxt,j=head[i];j!=-1;j=nxt){ Edge &e = edges[j]; nxt = e.nxt; if (e.to==s||e.to==t) continue ; if (!e.cap)continue; if (e.flow==e.cap)gg.AddEdge(e.to,e.from); else gg.AddEdge(e.from, e.to); } } } } g ;
费用流(SPFA)
#include<queue> #include<cmath> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long LL; const int N = 2007; const int INF=0x3f3f3f3f; const double EPS = 1e-6; struct MCMF{ struct Edge{ int from,to,cap,flow,nxt; double cost; Edge(){} Edge(int x,int y,int z,int u,double v,int n){ from=x;to=y;cap=z;flow=u;cost=v;nxt=n; } }edges[N]; int E,head[N]; int n,s,t,inq[N],p[N],a[N]; double d[N]; inline void Init(int n,int s,int t){ this->n = n; E = -1; this->s = s; this->t = t; memset(head,-1,sizeof(head)); } inline void AddEdge(int f,int t,int c,double w){ edges[++E] = Edge(f,t,c,0, w,head[f]); head[f] = E; edges[++E] = Edge(t,f,0,0,-w,head[t]); head[t] = E; } bool spfa(int s,int t,int flow,double &cost){ for (int i=0;i<=n;i++)d[i]=INF; memset(inq,0,sizeof(inq)); d[s]=0;inq[s]=1;p[s]=0;a[s]=INF; queue<int>Q;Q.push(s); for (;!Q.empty();){ int nxt, u =Q.front();Q.pop();inq[u]=0; for (int i=head[u];i!=-1;i=nxt){ Edge &e = edges[i]; nxt = e.nxt; if (e.cap<=e.flow||d[e.to]<=d[u]+e.cost)continue; d[e.to] = d[u] + e.cost; p[e.to] = i; a[e.to] = min(a[u],e.cap-e.flow); if (!inq[e.to]){Q.push(e.to);inq[e.to]=1;} } } if (d[t]==INF)return 0;//false flow += a[t]; cost += (double)d[t]*(double)a[t]; //printf("cost=%.2lf\n",cost); for (int u=t;u!=s;u=edges[p[u]].from){ edges[p[u] ].flow += a[t]; edges[p[u]^1].flow -= a[t]; } return 1;//true } //需要保证初始网络中没有负权 double mcmf(){ int flow =0; double cost = 0; for (;spfa(s,t,flow,cost);); return cost; }//MinCostMaxFlow } g ;
强连通分量Tarjan
hdu5934
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; const LL N = 1e3 + 7; const LL INF = 1e8 + 7; struct bomb{ LL x, y, r, c; void read(){ scanf("%lld%lld%lld%lld", &x, &y, &r, &c); } } bombs[N]; LL sqr(LL x){return x*x;} double dist(LL i, LL j){ return sqrt(sqr(bombs[i].x - bombs[j].x) +sqr(bombs[i].y - bombs[j].y)); } LL n; struct tarjan //复杂度O(N+M) { const static LL MAXN = N; //点数 const static LL MAXM = N*N;//边数 struct Edge{ LL to,next; } edge[MAXM]; LL head[MAXN], tot; LL Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];//Belong数组的值是1~scc LL Index,top; LL scc;//强连通分量的个数 LL num[MAXN];//各个强连通分量包含点的个数,数组编号1~scc //num数组不一定需要,结合实际情况 bool Instack[MAXN]; void addedge(LL u,LL v){ edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot++; } void Tarjan(LL u){ LL v; Low[u] = DFN[u] = ++Index; Stack[top++] = u; Instack[u] = true; for(LL i = head[u]; ~i; i = edge[i].next){ v = edge[i].to; if( !DFN[v] ){ Tarjan(v); Low[u] = min(Low[u], Low[v]); } else if(Instack[v] && Low[u] > DFN[v]) Low[u] = DFN[v]; } if(Low[u] == DFN[u]){ scc++; do{ v = Stack[--top]; Instack[v] = false; Belong[v] = scc; num[scc]++; }while( v != u); } } LL in[MAXN]; LL solve(LL N){ memset(DFN, 0, sizeof(DFN)); memset(num, 0, sizeof(num)); memset(Instack, 0, sizeof(Instack)); Index = scc = top = 0; for(LL i = 1; i <= N; i++) if(!DFN[i]) Tarjan(i); //for this problem memset(in, 0, sizeof(in)); for (LL u = 1; u <= n; u++){ for (LL e = head[u]; ~e; e = edge[e].next){ LL v = edge[e].to; if (Belong[u] != Belong[v]) in[Belong[v]]++; } } LL ans = 0; for (LL i = 1; i <= scc; i++) if (in[i] == 0){ LL cost = INF; for (LL j = 1; j <= n; j++) if (Belong[j] == i){ cost = min(cost, bombs[j].c); } ans += cost; } return ans; } void init(){ tot = 0; memset(head, -1, sizeof(head)); } } g; int main(){ //freopen("in.txt", "r", stdin); LL T; scanf("%lld", &T); for (LL cas = 1; cas <= T; cas++){ printf("Case #%lld: ", cas); scanf("%lld", &n); for (LL i = 1; i <= n; i++){ bombs[i].read(); } g.init(); for (LL i = 1; i < n; i++){ for (LL j = i+1; j <= n; j++){ double d = dist(i, j); if (bombs[i].r >= d) g.addedge(i, j); if (bombs[j].r >= d) g.addedge(j, i); } } LL ans = g.solve(n); printf("%lld\n", ans); } return 0; }
倍增LCA + 最大生成树
#include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int INF=0x3f3f3f3f; const int N = 1e5 + 5; int n,m; struct gragh{ struct Edge{ int from,to,w; Edge(){} Edge(int x,int y,int z):from(x),to(y),w(z){} bool operator < (const Edge& a)const{ return w < a.w; } }edges[N],be[N]; int E,f[N],fa[N][20],di[N][20],dep[N]; bool vis[N]; vector<int >G[N]; int F(int x){//鼯 return f[x]==x?x:(f[x]=F(f[x])); } inline void link(int x,int y,int z){ edges[++E]=Edge(x,y,z); G[x].push_back(E); } void build(){ E=0; for (int i=1;i<=n;i++)G[i].clear(); int x,y,z; for (int i=1;i<=n;i++)f[i]=i; for (int i=1;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); be[i]=Edge(x,y,z); f[F(x)]=F(y); } } void kruskal(){ int treenum = 0;//forests memset(vis,0,sizeof(vis)); for (int i=1;i<=n;i++)if (!vis[F(i)]){ treenum++;vis[F(i)]=1; } for (int i=1;i<=n;i++)f[i]=i; sort(be+1,be+m+1); int cnt = 0; for (int i=m;i>=1;i--){ int x = be[i].from; int y = be[i].to ; if (F(x)==F(y))continue; f[F(x)]=F(y); cnt++; link(x,y,be[i].w); link(y,x,be[i].w); if (cnt==n-treenum)break; } } void dfs(int x){ vis[x] = 1; for (int i=1;i<=17;i++){ if(dep[x]<(1<<i))break; fa[x][i]=fa[fa[x][i-1]][i-1]; di[x][i]=min(di[x][i-1],di[fa[x][i-1]][i-1]); } for (int i=0;i<G[x].size();i++){ Edge e = edges[G[x][i]]; if (vis[e.to])continue; fa[e.to][0] = x; di[e.to][0] = e.w; dep[e.to] = dep[x]+1; dfs(e.to); } } int lca(int x,int y){ if (dep[x]<dep[y])swap(x,y); int t = dep[x] - dep[y]; for (int i=0;i<=17;i++) if ((1<<i)&t) x = fa[x][i]; for (int i=17;i>=0;i--) if (fa[x][i]!=fa[y][i]){ x=fa[x][i];y=fa[y][i]; } if (x==y)return x; return fa[x][0]; } int ask(int x,int f){//f:father int ans = INF; int t = dep[x]-dep[f]; for (int i=0;i<=17;i++)if(t&(1<<i)){ ans=min(ans,di[x][i]); x = fa[x][i]; } return ans; } void work(){ build(); kruskal(); memset(vis,0,sizeof(vis)); for (int i=1;i<=n;i++)if(!vis[i])dfs(i); int q,x,y; scanf("%d",&q); while (q--){ scanf("%d%d",&x,&y); if (F(x)!=F(y))puts("-1"); else { int t = lca(x,y); x = ask(x,t); y = ask(y,t); printf("%d\n",min(x,y)); } } } }g; int main(){ //freopen("truck.in","r",stdin); //freopen("truck.out","w",stdout); for (;~scanf("%d%d",&n,&m);)g.work(); return 0; }
树链剖分
struct TreeChain{ struct Edge{ int from, to, nxt; Edge(){} Edge(int u, int v, int n): from(u), to(v), nxt(n){} }edges[N]; int n, E, head[N]; int tim; int siz[N]; //用来保存以x为根的子树节点个数 int top[N]; //用来保存当前节点的所在链的顶端节点 int son[N]; //用来保存重儿子 int dep[N]; //用来保存当前节点的深度 int fa[N]; //用来保存当前节点的父亲 int tid[N]; //用来保存树中每个节点剖分后的新编号,线段树 int Rank[N];//tid反向数组,不一定需要 inline void AddEdge(int f, int t){ edges[++E] = Edge(f, t, head[f]); head[f] = E; } inline void Init(int n){ tim = 0; this -> n = n ; E = -1; for (int i = 0; i <= n; i++) head[i] = -1; for (int i = 0; i <= n; i++) son[i] = -1; } void dfs1(int u, int father, int d){ dep[u] = d; fa[u] = father; siz[u] = 1; int nxt; for(int i = head[u]; i != -1; i = nxt){ Edge &e = edges[i]; nxt = e.nxt; if (e.to == father) continue; dfs1(e.to, u, d + 1); siz[u] += siz[e.to]; if(son[u]==-1 || siz[e.to] > siz[son[u]]) son[u] = e.to; } } void dfs2(int u, int tp){ top[u] = tp; tid[u] = ++tim; Rank[tid[u]] = u; if (son[u] == -1) return; dfs2(son[u], tp); int nxt; for(int i = head[u]; i != -1; i = nxt){ Edge &e = edges[i]; nxt = e.nxt; if(e.to == son[u] || e.to == fa[u]) continue; dfs2(e.to, e.to); } } LL query(int u, int v){ int f1 = top[u], f2 = top[v]; LL tmp = 0; for (; f1 != f2;){ if (dep[f1] < dep[f2]){ swap(f1, f2); swap(u, v); } tmp += T.query(tid[f1], tid[u]); u = fa[f1]; f1 = top[u]; } if (dep[u] > dep[v]) swap(u, v); return tmp + T.query(tid[u], tid[v]); } } g ;
树分治
poj1741模板题
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N = 1e4 + 7; const int INF = 0x3f3f3f3f; int n, k, ans; struct Edge{ int from, to, w, nxt; Edge(){} Edge (int f, int t, int _w, int n):from(f),to(t),w(_w),nxt(n){} } edges[N * 2]; bool vis[N]; int head[N], E, siz[N], dep[N]; void Init(){ E = 0; memset(head, -1, sizeof(head)); memset(vis, false, sizeof(vis)); } void AddEdge(int u,int v,int w){ edges[E] = Edge(u, v, w, head[u]); head[u] = E++; } int dfssize(int u, int pre){ siz[u] = 1; for(int i = head[u]; i != -1; i = edges[i].nxt){ Edge &e = edges[i]; if(e.to == pre || vis[e.to])continue; siz[u] += dfssize(e.to, u); } return siz[u]; } //找重心 void dfsroot(int u, int pre, int totnum, int &minn, int &root){ int maxx = totnum - siz[u]; for (int i = head[u]; i != -1;i = edges[i].nxt){ Edge &e = edges[i]; if(e.to == pre || vis[e.to]) continue; dfsroot(e.to, u, totnum, minn, root); maxx = max(maxx, siz[e.to]); } if(maxx < minn){minn = maxx; root = u;} } //求每个点离重心的距离 void dfsdep(int u,int pre,int dist, int &num){ dep[num++] = dist; for(int i = head[u]; i != -1; i = edges[i].nxt){ Edge &e = edges[i]; if(e.to == pre || vis[e.to])continue; dfsdep(e.to, u, dist + e.w, num); } } //计算以u为根的子树中有多少点对的距离小于等于K int calc(int u, int d){ //printf("calc(%d, %d)\n", u, d); int ans = 0, num = 0; dfsdep(u, -1, d, num); sort(dep, dep + num); int i = 0, j = num - 1; for (; i < j; i++){ while (dep[i]+dep[j]>k && i<j) j--; ans += j - i; } return ans; } void solve(int u){ int Max = N, root, minn = INF; int totnum = dfssize(u, -1); dfsroot(u, -1, totnum, minn, root); ans += calc(root, 0); vis[root] = 1; for(int i = head[root]; i != -1; i = edges[i].nxt){ Edge &e = edges[i]; if (vis[e.to]) continue; ans -= calc(e.to, e.w); solve(e.to); } } int main(){ ///freopen("in.txt","r",stdin); int u, v, w; for (; ~scanf("%d%d", &n, &k) && (n|k);){ Init(); for(int i = 1; i < n; i++){ scanf("%d%d%d", &u, &v, &w); AddEdge(u, v, w); AddEdge(v, u, w); } ans = 0; solve(1); printf("%d\n", ans); } return 0; }
图的割点、桥和双连通分支的基本概念
[点连通度与边连通度]
在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以
后,原图变成多个连通块,就称这个点集为 割点集合。一个图的 点连通度的定义为,最小割点集合中的顶
点数。
类似的,如果有一个边集合,删除这个边集合以后,原图变成多个连通块,就称这个点集为 割边集合。一
个图的 边连通度的定义为,最小割边集合中的边数。
[双连通图、割点与桥]
如果一个无向连通图的点连通度大于 1,则称该图是 点双连通的(point biconnected),简称 双连通或 重连通。
一个图有割点,当且仅当这个图的点连通度为 1,则割点集合的唯一元素被称为 割点(cut point),又叫 关节
点(articulation point)。
如果一个无向连通图的边连通度大于 1,则称该图是 边双连通的(edge biconnected),简称双连通或重连通。
一个图有桥,当且仅当这个图的边连通度为 1,则割边集合的唯一元素被称为 桥(bridge),又叫 关节边
(articulation edge) 。
可以看出,点双连通与边双连通都可以简称为双连通,它们之间是有着某种联系的,下文中提到的双连通,
均既可指点双连通,又可指边双连通。
[双连通分支]
在图 G 的所有子图 G’中,如果 G’是双连通的,则称 G’为 双连通子图。如果一个双连通子图 G’它不是任何一
个双连通子图的真子集,则 G’为 极大双连 通子图。 双连通分支(biconnected component),或 重连通分支,
就是图的极大双连通子图。特殊的,点双连通分支又叫做 块。
[求割点与桥]
该算法是 R.Tarjan 发明的。对图深度优先搜索,定义DFS(u)为u 在搜索树(以下简称为树)中被遍历到的次
序号。定义 Low(u)为u 或u 的子树中能通过非父子边追溯到的最早的节点,即 DFS 序号最小的节点。根据
定义,则有:
Low(u)=Min { DFS(u) DFS(v) (u,v)为后向边(返祖边) 等价于 DFS(v)
动态规划
各种背包
背包
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 100007; struct node { int v,w,n; node(){} node(int x,int y,int z){ v=x,w=y,n=z; } }a[N]; int f[N]; int main(){ //freopen("fuck.in","r",stdin); int cash,n,x,y; for (;~scanf("%d%d",&cash,&n);){ int A = 0; for(int i=1;i<=n;i++){ scanf("%d%d",&x,&y); for (int t=0;(1<<t)<x;t++){ int tt=1<<t; a[++A]=node(y*tt,y*tt,1); x -= tt; } if (x)a[++A]=node(y*x,y*x,1); } memset(f,0,sizeof(f));//01背包 for (int i=1;i<=A;i++) for (int j=cash;j>=a[i].v;j--) f[j]=max(f[j],f[j-a[i].v]+a[i].w); int ans = 0;//get ans for (int i=0;i<=cash;i++) ans=max(ans,f[i]); printf("%d\n",ans); } return 0; }
多重背包通用模板(单调队列)
int f[N]; int va[N], vb[N];//MAX_V void pack(int V,int v,int w,int n){ if (n==0||v==0) return; if (n==1){//01背包 for (int i=V;i>=v;--i) f[i]=max(f[i],f[i-v]+w); return; } if (n*v>=V-v+1){//多重背包(n >= V / v) for (int i=v;i<=V;++i) f[i]=max(f[i],f[i-v]+w); return; } for (int j = 0 ; j < v ; ++j ){ int *pb = va, *pe = va - 1; int *qb = vb, *qe = vb - 1; for (int k=j,i=0;k<=V;k+=v,++i){ if (pe==pb+n){ if(*pb == *qb) ++qb; ++pb; } int tt = f[k] - i * w; *++pe = tt; while (qe>=qb&& *qe<tt)--qe; *++qe = tt; f[k] = *qb + i * w; } } } //主程序调用 memset(f,0,sizeof(f)); //pack for (int i=1;i<=n;i++) pack(cash,a[i].v,a[i].w,a[i].n); int ans = 0; //getAns for (int i=0;i<=cash;i++) ans=max(ans,f[i]); printf("%d\n",ans);
小价值大重量背包问题
#include <stdio.h> #include <iostream> #include <algorithm> #include <cmath> #include <ctime> using namespace std; const int MAXN = 1005, MAXV = 1000005; int f[MAXV]; int v[MAXN]; int w[MAXN]; const int INF = 0x3f3f3f3f; int main(){ int T; cin >> T; while(T--) { int n, V; cin >> n >> V; fill(f, f + MAXV, INF); f[0] = 0; for(int i = 0; i < n; i++) scanf("%d", v + i); for(int i = 0; i < n; i++) scanf("%d", w + i); for(int i = 0; i < n; i++) { for(int j = MAXV - 1; j >= v[i] ; j--) { f[j] = min(f[j], f[j - v[i]] + w[i]); } } int ans = -1; for(int i = MAXV - 1; i >= 0; i--) { if (f[i] <= V) { ans = i; break; } } if (ans != -1) printf("%d\n", ans); else printf("No solution\n"); } return 0; }
输出LCS序列
#include <string.h> #include <stdio.h> #include <iostream> #include <algorithm> #include <stdlib.h> #include <string> #include <vector> using namespace std; string s1,s2; string lcs2(string s1,string s2){ if(s1==""||s2=="")return ""; int m=s1.size() + 1; int n=s2.size() + 1; printf("%d %d\n",m,n); int lcs[m][n]; memset(lcs,0,sizeof(lcs)); for(int i=1;i<m;i++) for(int j=1;j<n;j++){ if(s1[i-1]==s2[j-1]) lcs[i][j]=lcs[i-1][j-1]+1; else lcs[i][j]=lcs[i-1][j]>=lcs[i][j-1]?lcs[i-1][j]:lcs[i][j-1];//取上侧或左侧的最大值 } int i=m-2; int j=n-2; string ss=""; while(i!=-1&&j!=-1) { if(s1[i]==s2[j]) { //printf("%c\n",s1[i]); ss+=s1[i]; i--; j--; } else { if(lcs[i+1][j+1]==lcs[i][j]) { i--; j--; } else { if(lcs[i][j+1]>=lcs[i+1][j]) i--; else j--; } } } reverse(ss.begin(),ss.end());//将字符串倒置 return ss; } int main(){ while(cin>>s1>>s2){ string s=lcs2(s1,s2); cout << s<<endl; } return 0; }
TSP旅行商问题.(状压DP)
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int MAXN = 16; const int INF = 0x3f3f3f3f; int n; int dp[1 << MAXN][MAXN]; int d[MAXN][MAXN]; int rec(int s, int v) { //记忆化 if (dp[s][v] >= 0) {return dp[s][v];} //已经访问过所有节点并返回零号节点 if (s == (1 << n) - 1 && v == 0) { return dp[s][v] = 0; } int res = INF; for (int u = 0; u < n; u++) { if (!(s >> u & 1)) { //下一步移动到顶点U res = min(res, rec(s | 1 << u, u) + d[v][u]); } } return dp[s][v] = res; } int solve() { memset(dp, -1, sizeof(dp)); printf("%d\n", rec(0, 0)); } void floyd() { for(int k = 0; k < n; k++) { for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { if (i == j) d[i][j] = 0; else d[i][j] = min(d[i][j], d[i][k] + d[k][j]); } } } } int main(){ int T; cin >> T; while(T--) { memset(d, INF, sizeof(d)); int m; cin >> n >> m; for(int i = 0; i < m; i++) { int x, y, v; scanf("%d%d%d", &x, &y, &v); if (d[x - 1][y - 1] > v) d[x - 1][y - 1] = d[y - 1][x - 1] = v; } //每个点只能走一次就是原本的TSP问题,可以走多次先求各点最短路转化为TSP //道理比如1 - 3, 1 - 2 那么 1 要走3次, TSP求不出 但是求一波Floyd以后, 3 - 2有条路了,相当于改变了图 floyd(); solve(); } return 0; }
DAG序列反向DP
#include<bits/stdc++.h> using namespace std; #define LL long long const int maxn = 1e5+7; const int mod = 1e9+7; vector<int>e[maxn]; LL a[maxn],b[maxn],d[maxn]; LL ans[maxn]; int n,m; int main(){ while(scanf("%d%d",&n,&m)!=EOF) { for(int i = 0;i<=n;i++) e[i].clear(); memset(d,0,sizeof(d)); memset(ans,0,sizeof(ans)); for(int i = 1;i<=n;i++) scanf("%lld%lld",&a[i],&b[i]); for(int i = 1;i<=m;i++){ int u,v; scanf("%d%d",&u,&v); e[v].push_back(u); d[u]++; } queue<int>q; for(int i = 1;i<=n;i++) if(d[i]==0)q.push(i); while(!q.empty()){ int u = q.front();q.pop(); for(int i = 0;i<e[u].size();i++){ int v = e[u][i]; ans[v]=(ans[v]+(ans[u]+b[u])%mod)%mod; if(--d[v]==0) q.push(v); } } LL res = 0; for(int i = 1;i<=n;i++) res = (res + 1LL*ans[i]*a[i]%mod)%mod; printf("%lld\n",res); } }
数位dp
/***************** 不要62 ******************/ #include<iostream> #include<cstdio> #include<cstring> #include<string> using namespace std; typedef int LL; LL a[30]; LL dp[30][2]; LL z[30] = {1}; LL n; LL dfs(LL pos, LL stat, bool limit) { if(pos == -1) return 1; if(!limit && dp[pos][stat] != -1) return dp[pos][stat]; LL up = limit ? a[pos] : 9; LL ans = 0; for(LL i = 0; i <= up; i++) { if(i == 2 && stat) continue; if(i == 4) continue; ans += dfs(pos - 1, i == 6, limit && i == a[pos]); } if(!limit) dp[pos][stat] = ans; return ans; } LL solve(LL x) { LL pos = 0; while(x) { a[pos++] = x % 10; x /= 10; } return dfs(pos - 1, 0, true); } int main() { memset(dp, -1, sizeof(dp)); LL n1, n2; while(scanf("%d%d", &n1, &n2), n1 + n2) { printf("%d\n", solve(n2) - solve(n1 - 1)); } return 0; } /***************** 只要49 ******************/ #include<iostream> #include<cstdio> #include<cstring> #include<string> using namespace std; typedef long long LL; LL a[100]; LL dp[100][2]; LL z[100] = {1}; LL n; LL dfs(LL pos, LL stat, bool limit) { if(pos == -1) return 0; if(!limit && dp[pos][stat] != -1) return dp[pos][stat]; LL up = limit ? a[pos] : 9; LL ans = 0; for(LL i = 0; i <= up; i++) { if(stat && i == 9) { ans += limit ? (n % z[pos] + 1) : z[pos]; } else { ans += dfs(pos - 1, i == 4, limit && i == a[pos]); } } if(!limit) dp[pos][stat] = ans; return ans; } LL solve(LL x) { LL pos = 0; while(x) { a[pos++] = x % 10; x /= 10; } return dfs(pos - 1, 0, true); } int main() { for(int i = 1;i < 30; i++) { z[i] = z[i - 1] * 10; } memset(dp, -1, sizeof(dp)); int t; cin >> t; while(t--) { cin >> n; cout << solve(n) << endl; } return 0; } 第二种方法 LL n, dp[25][3]; //dp[i][j]:长度为i,状态为j int digit[25]; //nstatus: 0:不含49, 1:不含49但末尾是4, 2 :含49 LL DFS(int pos, int status, int limit) { if(pos <= 0) // 如果到了已经枚举了最后一位,并且在枚举的过程中有49序列出现 return status==2;//注意是 == if(!limit && dp[pos][status]!=-1) //对于有限制的询问我们是不能够记忆化的 return dp[pos][status]; LL ans = 0; int End = limit?digit[pos]:9; // 确定这一位的上限是多少 for(int i = 0; i <= End; i++) // 每一位有这么多的选择 { int nstatus = status; // 有点else s = statu 的意思 if(status==0 && i==4)//高位不含49,并且末尾不是4 ,现在末尾添4返回1状态 nstatus = 1; else if(status==1 && i!=4 && i!=9)//高位不含49,且末尾是4,现在末尾添加的不是4返回0状态 nstatus = 0; else if(status==1 && i==9)//高位不含49,且末尾是4,现在末尾添加9返回2状态 nstatus = 2; ans+=DFS(pos-1, nstatus, limit && i==End); } if(!limit) dp[pos][status]=ans; return ans; } 第三种方法·没有DFS的纯DP LL dp[27][3]; int c[27]; //dp[i][j]:长度为i的数的第j种状态 //dp[i][0]:长度为i但是不包含49的方案数 //dp[i][1]:长度为i且不含49但是以9开头的数字的方案数 //dp[i][2]:长度为i且包含49的方案数 void init() { memset(dp,0,sizeof(dp)); dp[0][0] = 1; for(int i = 1; i <= 20; i++) { dp[i][0] = dp[i-1][0]*10-dp[i-1][1]; dp[i][1] = dp[i-1][0]*1; dp[i][2] = dp[i-1][2]*10+dp[i-1][1]; } } /***************** 被13整除且包含“13” ******************/ #include<iostream> #include<cstdio> #include<cstring> #include<string> using namespace std; typedef int LL; LL a[30]; LL dp[30][15][3]; LL z[30] = {1}; LL n; LL dfs(LL pos, LL mod, LL stat, bool limit) { if(pos == -1) return mod == 0 && stat == 2; if(!limit && dp[pos][mod][stat] != -1) return dp[pos][mod][stat]; LL up = limit ? a[pos] : 9; LL ans = 0; for(LL i = 0; i <= up; i++) { LL ns = stat; if(stat == 0 && i == 1) ns = 1; if(stat == 1 && i != 1) ns = 0; if(stat == 1 && i == 3) ns = 2; ans += dfs(pos - 1, (mod * 10 + i) % 13, ns, limit && i == a[pos]); } if(!limit) dp[pos][mod][stat] = ans; return ans; } LL solve(LL x) { LL pos = 0; while(x) { a[pos++] = x % 10; x /= 10; } return dfs(pos - 1, 0, 0, true); } int main() { memset(dp, -1, sizeof(dp)); for(LL i = 1; i < 30; i++) { z[i] = z[i - 1] * 10; } while(cin >> n) { cout << solve(n)<< endl; } return 0; }
计算几何(见红书)
其他
C++高精度
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <algorithm> using namespace std; const int MAXN = 600; char s1[MAXN], s2[MAXN]; struct bign { int len, s[MAXN]; bign (){ memset(s, 0, sizeof(s)); len = 1; } bign (int num) { *this = num; } bign (const char *num) { *this = num; } bign operator = (const int num){ char s[MAXN]; sprintf(s, "%d", num); *this = s; return *this; } bign operator = (const char *num){ for(int i = 0; num[i] == '0'; num++) ; //ȥǰµ¼0 len = strlen(num); for(int i = 0; i < len; i++) s[i] = num[len-i-1] - '0'; return *this; } bign operator + (const bign &b) const //+ { bign c; c.len = 0; for(int i = 0, g = 0; g || i < max(len, b.len); i++){ int x = g; if(i < len) x += s[i]; if(i < b.len) x += b.s[i]; c.s[c.len++] = x % 10; g = x / 10; } return c; } bign operator += (const bign &b){ *this = *this + b; return *this; } void clean(){ while(len > 1 && !s[len-1]) len--; } bign operator * (const bign &b) //* { bign c; c.len = len + b.len; for(int i = 0; i < len; i++){ for(int j = 0; j < b.len; j++){ c.s[i+j] += s[i] * b.s[j]; } } for(int i = 0; i < c.len; i++){ c.s[i+1] += c.s[i]/10; c.s[i] %= 10; } c.clean(); return c; } bign operator *= (const bign &b){ *this = *this * b; return *this; } bign operator - (const bign &b){ bign c; c.len = 0; for(int i = 0, g = 0; i < len; i++){ int x = s[i] - g; if(i < b.len) x -= b.s[i]; if(x >= 0) g = 0; else{ g = 1; x += 10; } c.s[c.len++] = x; } c.clean(); return c; } bign operator -= (const bign &b){ *this = *this - b; return *this; } bign operator / (const bign &b){ bign c, f = 0; for(int i = len-1; i >= 0; i--){ f = f*10; f.s[0] = s[i]; while(f > b || f == b) { f -= b; c.s[i]++; } } c.len = len; c.clean(); return c; } bign operator /= (const bign &b){ *this = *this / b; return *this; } bign operator % (const bign &b){ bign r = *this / b; r = *this - r*b; return r; } bign operator %= (const bign &b){ *this = *this % b; return *this; } bool operator < (const bign &b){ if(len != b.len) return len < b.len; for(int i = len-1; i >= 0; i--){ if(s[i] != b.s[i]) return s[i] < b.s[i]; } return false; } bool operator > (const bign &b){ if(len != b.len) return len > b.len; for(int i = len-1; i >= 0; i--) { if(s[i] != b.s[i]) return s[i] > b.s[i]; } return false; } bool operator == (const bign &b){ return !(*this > b) && !(*this < b); } string str() const{ string res = ""; for(int i = 0; i < len; i++) res = char(s[i]+'0') + res; return res; } }; int main(){ bign a, b, c; while(scanf("%s %s", s1, s2) != EOF) { a = bign(s1); b = bign(s2); c = a / b; cout << c.str() << endl; } return 0; }
Java大整数
—:在java中的基本头文件(java中叫包) import java.io.* importjava.util.* 我们所用的输入scanner在这个包中 importjava.math.* 我们下面要用到的BigInteger就这这个包中 二:输入与输出 读入 Scanner cin=new Scanner(System.in) While(cin.hasNext()) //相当于C语言中的!=EOF n = cin.nextInt(); //输入一个int型整数 n = cin.nextBigInteger(); //输入一个大整数 System.out.print(n); //输出n但不换行 System.out.println(); //换行 System.out.println(n); //输出n并换行 System.out.printf(“%d\n”,n); //类似C语言中的输出 三:定义变量 定义单个变量: int a,b,c; //和C++ 中无区别 BigInteger a; //定义大数变量a BigIntegerb= new BigInteger("2"); //定义大数变量 b赋值为 2; BigDecimaln; //定义大浮点数类 n; 定于数组: int a[]= new int[10] //定义长度为10的数组a BigInteger b[] =new BigInteger[100] //定义长度为100的数组a 四:表示范围 布尔型 boolean 1 true,false false 字节型 byte 8 -128-127 0 字符型 char 16 ‘\u000’-\uffff ‘\u0000’ 短整型 short 16 -32768-32767 0 整型 int 32 -2147483648,2147483647 0 长整型 long 64 -9.22E18,9.22E18 0 浮点型 float 32 1.4E-45-3.4028E+38 0.0 双精度型 double 64 4.9E-324,1.7977E+308 0.0 BigInteger任意大的数,原则上只要你的计算机内存足够大,可以有无限位 五:常用的一些操作 A=BigInteger.ONE; //把0赋给A B=BigInteger. valueOf (3); //把3赋给B; A[i]=BigInteger. valueOf (10); //把10赋给A[i] c=a.add(b) //把a与b相加并赋给c c=a.subtract(b) //把a与b相减并赋给c c=a.multiply(b) //把a与b相乘并赋给c c=a.divide(b) //把a与b相除并赋给c c=a.mod(b) // 相当于a%b a.pow(b) //相当于a^b a.compareTo(b): //根据该数值是小于、等于、或大于a 返回 -1、0 或 1; a.equals(b): //判断两数是否相等,也可以用compareTo来代替; a.min(b),a.max(b): //取两个数的较小、大者;
例题:hdu5920
题意:求一个数用50个以内的回文数加起来的方案
import java.math.*; //import java.io.*; import java.util.*; public class Main { public static BigInteger fanzhuan(BigInteger n){ //System.out.println("fanzhuan" + n); int k = String.valueOf(n).length(); BigInteger ret = BigInteger.ZERO; for (int i=1;i<=k;i++){ ret = ret.add(n.mod(BigInteger.TEN)); ret = ret.multiply(BigInteger.TEN); n = n.divide(BigInteger.TEN); } ret = ret.divide(BigInteger.TEN); return ret; } public static void main(String[] argv){ Scanner cin = new Scanner(System.in); int T =cin.nextInt(); for (int cas=1;cas<=T;cas++){ BigInteger n = cin.nextBigInteger(); int N = String.valueOf(n).length(); int A = 0; BigInteger ans[] = new BigInteger[55]; for (;N>1;){ //System.out.println("n=" + n); BigInteger one0 = BigInteger.TEN.pow(N>>1); BigInteger half = n.divide(one0); if (N<=2){ if (N==1){ ans[++A] = n; n = BigInteger.ZERO; break; }else {//2 wei if (n.compareTo(BigInteger.valueOf(19))==0){ ans[++A] = BigInteger.valueOf(11); ans[++A] = BigInteger.valueOf( 8); n = BigInteger.ZERO; break; }else if (n.compareTo(BigInteger.valueOf(19))==-1){ ans[++A] = BigInteger.valueOf(9); ans[++A] = n.subtract(BigInteger.valueOf(9)); n = BigInteger.ZERO; break; }//else continue; } } half = half.subtract(BigInteger.ONE); //System.out.println("half=" + half); BigInteger fan = fanzhuan(half); if (N%2>0) fan = fan.mod(one0); //System.out.println("fan=" + fan); BigInteger jan = half.multiply(one0).add(fan); //System.out.println("jan=" + jan); ans[++A] = jan ; n = n.subtract(jan); N = String.valueOf(n).length(); } if (n.compareTo(BigInteger.ZERO)==1)ans[++A] = n; System.out.printf("Case #%d:\n%d\n",cas,A); for (int i=1;i<=A;i++){ System.out.println(ans[i]); } } cin.close(); } }
头文件
#include <iostream> #pragma comment(linker, "/STACK:1024000000,1024000000") #include <stdio.h> #include <fstream> #include <iomanip> #include <cmath> #include <string> #include <string.h> #include <sstream> #include <cctype> #include <climits> #include <set> #include <map> #include <deque> #include <queue> #include <vector> #include <iterator> #include <algorithm> #include <stack> #include <functional> /*int类型最大值INT_MAX,short最大值为SHORT_MAX long long最大值为LONG_LONG_MAX*/ //cout << "OK" << endl; #define _clr(x,y) memset(x,y,sizeof(x)) #define _inf(x) memset(x,0x3f,sizeof(x)) #define pb push_back #define mp make_pair #define FORD(i,a,b) for (int i=(a); i<=(b); i++) #define FORP(i,a,b) for (int i=(a); i>=(b); i--) #define REP(i,n) for (int i=0; i<(n); i++) using namespace std; const int INF = 0x3f3f3f3f; const double eps = 1e-8; const double EULER = 0.577215664901532860; const double PI = 3.1415926535897932384626; const double E = 2.71828182845904523536028; typedef long long LL;
输入挂
const int BUFSIZE = 100 * 1024 * 1024; char Buf[BUFSIZE + 1], *buf = Buf; template<class T> void read(T &a){ for(a=0; *buf<'0' || *buf>'9'; buf++); while(*buf>='0' && *buf<='9'){ a = a*10+(*buf-'0'); buf++; } }
对拍模板,freopen
FOR ACM OI
在linux的shell脚本对拍命令
执行方法:在终端下,进入当前目录,输入”sh ./nick.sh”,(其中nick.sh为当前shell脚本名)
ubuntu14.04下实测成功
while true; do ./make>tmp.in #出数据 ./tmp<tmp.in>tmp.out #被测程序 ./tmp2<tmp.in>tmp2.out #正确(暴力)程序 if diff tmp.out tmp2.out; then #比较两个输出文件 printf AC #结果相同显示AC else echo WA #结果不同显示WA,并退出 #cat tmp.out tmp2.out exit 0 fi #if的结束标志,与C语言相反,0为真 done # while的结束标志 #BY NICK WONG 2014-08-29 #在终端下,进入当前目录,输入"sh ./nick.sh",(其中nick.sh为当前shell脚本名) '#'表示单行注释 #diff在两文件相同时返回空串
freopen的关闭
freopen("in.txt", "r", stdin); fclose(stdin); freopen("CON", "r", stdin); int m; cin >> m; cout << m;
/*
windows 下对拍
:again D:\cb-work4\gen\bin\Debug\gen.exe D:\cb-work4\duiA\bin\Debug\duiA.exe D:\cb-work4\duiB\bin\Debug\duiB.exe fc C:\Users\admin\Desktop\duipai\out1.txt C:\Users\admin\Desktop\duipai\out2.txt if not errorlevel 1 goto again pause
linux 下对拍
if diff test.out test.ans;then echo AC else echo WA exit 0 fi done */
来源:https://www.cnblogs.com/cww97/p/7533949.html