东拼西凑的模板·持续更新中
一.常用算法
文中代码大量来自ACM算法模板 · 一些常用的算法模板-模板合集(稍加修改)
1.1 快速幂
1 ll qpow(ll x, ll n , ll mod) 2 { 3 ll ans=1; 4 while(n){ 5 if(n&1){ 6 ans=(ans*x)%mod; 7 n--; 8 } 9 x=(x*x)%mod; 10 n>>=1; 11 } 12 return ans; 13 }
1.2 gcd
1 ll gcd(ll a,ll b){ 2 return b==0?a:gcd(b,a%b); 3 }
1.3 大数乘法
1 long long quick_multiply(long long a, long long b, long long mod) { 2 long long result = 0; 3 while (b) { 4 result = (result + (b % 2 * a) % mod) % mod; 5 a = a * 2 % mod; 6 b = b / 2; 7 } 8 }
1 for(i=0; i<LA-1; i++) 2 for(j=0; j<LB-1; j++) 3 c[i+j]+=a[i]*b[j]; 4 5 for(i=0; i<LA+LB; i++) 6 if(c[i]>=10) 7 { 8 c[i+1]+=c[i]/10; 9 c[i]%=10; 10 }
1 //https://www.cnblogs.com/king-ding/p/bigIntegerMul.html 2 #include<stdio.h> 3 #include<string.h> 4 #include<malloc.h> 5 6 #define and && /**************/ 7 #define or || /* python风格 */ 8 #define not ! /* */ 9 #define Int(X) (X - '0') /**************/ 10 11 int *multiBigInteger(const char *, const char *); 12 int checkNum(const char *); 13 14 int main(void) 15 { 16 char num1[100] = {'\0'}, num2[100] = {'\0'}; 17 printf("Please input two nunber(less than 100 digits):\n> "); 18 while(scanf("%s%s", num1, num2) != EOF) 19 { 20 int *result = NULL; 21 int i, change = 0; 22 //对输入的数据进行检验 23 if(strlen(num1) > 100 or strlen(num2) > 100) 24 { 25 printf("per number must less than 100 digits\n"); 26 return 1; 27 } 28 29 if(checkNum(num1) or checkNum(num2)) 30 { 31 printf("ERROR: input must be an Integer\n"); 32 return 1; 33 } 34 35 printf("num1:\t%s\nnum2:\t%s\n", num1, num2); 36 37 result = multiBigInteger(num1, num2); 38 39 /* 输出结果result,result[0]保存着result的长度, 40 * 所以下标要从1开始 */ 41 printf("result:\t"); 42 for(i = 1; i <= result[0]; i++) 43 { 44 if(result[i] != 0) //这一步用来去掉前导0,第一位为0跳过不输出 45 change = 1; 46 if(not change) 47 { 48 if(i > 1) //这一步用来判断结果是否为0, 49 { //如果结果第二位还是0,就判断为0 50 printf("0"); 51 break; 52 } 53 continue; 54 } 55 printf("%d", result[i]); 56 } 57 printf("\n"); 58 printf("\nPlease input two nunber(less than 100 digits):\n> "); 59 } 60 return 0; 61 } 62 63 //用于检测输入的是否是数字,如果是就返回0,不是就返回1 64 int checkNum(const char *num) 65 { 66 int i; 67 for(i = 0; (size_t)i < strlen(num); i++) 68 { 69 if(num[i] < '0' or num[i] > '9') 70 { 71 return 1; 72 } 73 } 74 return 0; 75 } 76 77 //返回结果result,为一片内存块,类似数组 78 int *multiBigInteger(const char *num1, const char *num2) 79 { 80 int *result = NULL; //用来保存最终结果 81 int num1Len = strlen(num1); //num1的长度 82 int num2Len = strlen(num2); //num2的长度 83 int resultLen; //结果的最大长度 84 int i, j; //循环计数器 85 resultLen = num1Len + num2Len; //结果长度最大为num1长度和num2长度之和 86 //初始化result为0 87 result = (int *)malloc((resultLen+1)*sizeof(int)); 88 memset(result, 0, (resultLen+1)*sizeof(int)); 89 90 result[0] = resultLen; //result的第一位是用来保存result的长度的。 91 /* num1乘以num2,由于这里采用先不进位的算法,所以算法是按从左到右 92 * 按顺序来乘,然后将每位的结果保存到result的每一位中,循环一次 93 * reult就从下一位开始求和。如下:(左边为正常算法,右边为本程序算法) 94 * 95 * 54321 | 54321 96 * × 123 | × 123 97 * ------- | -------- 98 * 162963 | 54321 99 * 108642 | 108642 100 * 54321 | 162963 101 * -------- | --------- 102 * 6681483 | 6681483 103 * 104 * */ 105 for(j = 0; j < num2Len; j++) 106 { 107 for(i = 0; i < num1Len; i++) 108 { 109 /* result第一位是用来保存result长度的,而第二位是保存结果最后的进位的 110 * 没有进位,则result[1]为0,所以每位相乘之和是从第三位(即result[2]) 111 * 开始。这里是本程序的比较巧妙的地方,需要仔细想想。 112 * */ 113 result[i+j+2] += Int(num1[i]) * Int(num2[j]); 114 } 115 } 116 117 /* 这个循环用来处理进位的,所以要从result的最后一位一直处理到首位。 118 * 要注意result的总长度是resultLen+1,有一位是保存result的长度,而 119 * C语言下标是从0开始,所以result的最后一位的下标就是resultLen,而 120 * 第一位就是1。*/ 121 for(i = resultLen; i > 1; i--) 122 { 123 result[i-1] += result[i]/10; 124 result[i] = result[i]%10; 125 } 126 printf("num1Len:%d\nnum2Len:%d\n", num1Len, num2Len); 127 return result; 128 } 129 130 大整数相乘2完整代码
1 //JAVA 大数相乘 2 import java.math.BigInteger; 3 import java.util.*; 4 import java.io.*; 5 6 public class Main 7 { 8 public static void main(String args[]) 9 { 10 Scanner cin = new Scanner(System.in); 11 BigInteger a = cin.nextBigInteger(); 12 BigInteger b = cin.nextBigInteger(); 13 BigInteger ans = a.multiply(b); 14 System.out.println(ans); 15 } 16 }
1.4高精度幂运算
1 //JAVA 高精度幂 2 import java.io.*; 3 import java.math.BigDecimal; 4 import java.util.*; 5 6 public class Main 7 { 8 public static void main(String args[]) 9 { 10 Scanner cin = new Scanner(System.in); 11 while(cin.hasNext()) 12 { 13 BigDecimal ans = cin.nextBigDecimal(); 14 int n = cin.nextInt(); 15 String res = ans.pow(n).stripTrailingZeros().toPlainString(); //整数去掉后面的0和小数点 16 if(res.startsWith("0")) //去掉前导0 17 { 18 res = res.substring(1); 19 } 20 System.out.println(res); 21 } 22 } 23 }
更多java大数使用见:https://blog.csdn.net/morejarphone/article/details/51884888
1.6 二分查找
1 int l=0,r=n; 2 while(l+1<r){ 3 int k=l+r>>1; 4 if(check(k))l=k; 5 else r=k; 6 }
1.7 矩阵快速幂
1 struct Matrix { 2 ll mat[3][3]; 3 }; 4 5 Matrix mul(Matrix a, Matrix b) { 6 Matrix ret; 7 for (int i = 0; i < 3; i++) 8 for (int j = 0; j < 3; j++) { 9 ret.mat[i][j] = 0; 10 for (int k = 0; k < 3; k++) { 11 // if(ret.mat[i][k]&&ret.mat[k][j]) 12 ret.mat[i][j] = (ret.mat[i][j] + a.mat[i][k] * b.mat[k][j] % mod) % mod; 13 } 14 } 15 return ret; 16 } 17 18 Matrix qpow(Matrix a, ll n) { 19 Matrix ret; 20 memset(ret.mat, 0, sizeof(ret.mat)); 21 ret.mat[0][0] = ret.mat[1][1] = ret.mat[2][2] = 1; 22 Matrix tmp = a; 23 while (n) { 24 if (n & 1)ret = mul(ret, tmp); 25 tmp = mul(tmp, tmp); 26 n >>= 1; 27 } 28 return ret; 29 }
1 //https://blog.csdn.net/wust_zzwh/article/details/52058209 2 const int N=10; 3 int tmp[N][N]; 4 void multi(int a[][N],int b[][N],int n) 5 { 6 memset(tmp,0,sizeof tmp); 7 for(int i=0;i<n;i++) 8 for(int j=0;j<n;j++) 9 for(int k=0;k<n;k++) 10 tmp[i][j]+=a[i][k]*b[k][j]; 11 for(int i=0;i<n;i++) 12 for(int j=0;j<n;j++) 13 a[i][j]=tmp[i][j]; 14 } 15 int res[N][N]; 16 void Pow(int a[][N],int n) 17 { 18 memset(res,0,sizeof res);//n是幂,N是矩阵大小 19 for(int i=0;i<N;i++) res[i][i]=1; 20 while(n) 21 { 22 if(n&1) 23 multi(res,a,N);//res=res*a;复制直接在multi里面实现了; 24 multi(a,a,N);//a=a*a 25 n>>=1; 26 } 27 }
#include"bits/stdc++.h" #define vec vector<long long> #define Mt vector<vec> using namespace std; const int mod = 1e9 + 7; int n; Mt mul(Mt &A,Mt &B){ Mt C(A.size(),vec(B[0].size())); for(int i=0;i<A.size();i++) for(int j=0;j<B[0].size();j++) for(int k=0;k<B.size();k++) C[i][j]=(C[i][j]+A[i][k]*B[k][j])%mod; return C; } long long qpow(long long x,long long n) { long long ans=1; while(n){ if(n&1){ ans=(ans*x)%mod; } n>>=1; x=(x*x)%mod; } return ans; } Mt qpow(Mt A,long long n){ Mt B(A.size(),vec(A.size())); for(int i=0;i<A.size();i++) B[i][i]=1; for(;n;n>>=1){if(n&1)B=mul(B,A);A=mul(A,A);} return B; } int main(){ while(cin>>n){ Mt X(4,vec(4));Mt Y(1,vec(4));Mt A(1,vec(5));Mt C(1,vec(5)); X[0][0]=1; X[0][1]=1;X[1][2]=1;X[2][0]=1; X[2][3]=1;X[3][0]=1; Y[0][0]=6;Y[0][1]=4;Y[0][2]=2;Y[0][3]=1; A[0][0]=17,A[0][1]=7,A[0][2]=2,A[0][3]=0;A[0][4]=0; if(n<=5){ printf("%lld\n",A[0][5-n]); } else{ X=qpow(X,n-3); C=mul(Y,X); printf("%lld\n",(qpow(2,n)-C[0][0]+mod)%mod ); } } return 0; }
1.8逆序数(归并排序)
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #define ll long long 6 using namespace std; 7 const int N=1e6+5; 8 int f[N],t[N]; 9 ll ans; 10 void Merge(int l,int m,int r) //左右两个表合并成一个表 11 { 12 int i=l,j=m+1,cnt=0; 13 while(i<=m && j<=r) 14 { 15 if(f[i]<=f[j]) 16 t[cnt++]=f[i++]; 17 else 18 { 19 ans+=m-i+1; 20 t[cnt++]=f[j++]; //核心代码,求解逆序数个数。 21 } 22 } 23 while(i<=m) //若左表不空 24 t[cnt++]=f[i++]; 25 while(j<=r) //若右表不空 26 t[cnt++]=f[j++]; 27 for(int k=0;k<cnt;) //修改原数组 28 f[l++]=t[k++]; 29 } 30 void Merge_sort(int l,int r) //归并排序 31 { 32 if(l==r) 33 return ; 34 else 35 { 36 int m=(l+r)>>1; 37 Merge_sort(l,m); 38 Merge_sort(m+1,r); 39 Merge(l,m,r); 40 } 41 } 42 int main() 43 { 44 int n; 45 ll x,y; 46 while(scanf("%d%lld%lld",&n,&x,&y)==3) 47 { 48 for(int i=0;i<n;i++) scanf("%d",&f[i]); 49 ans=0; 50 Merge_sort(0,n-1); 51 printf("%lld\n",min<ll>(x,y)*ans); 52 } 53 return 0; 54 }
1.9 全排列遍历
1 STL的全排列支持可重集 2 int n; 3 int p[maxn]; 4 void solve() 5 { 6 scanf("%d",&n); 7 for(int i = 0;i<n;i++) 8 { 9 scanf("%d",&p[i]); 10 } 11 do 12 { 13 for(int i = 0;i<n;i++) printf("%d ",p[i]); 14 printf("\n"); 15 16 }while(next_permutation(p,p+n)); 17 }
1.10 lowbit(二进制最低位)
int lowbit(int x){ return x&(-x); }
1.11 组合数打表预处理
1 void init() 2 { 3 Q[0]=1,Q[1]=2; 4 C[1][0] = C[1][1] = 1; 5 for (int i = 2; i < 110; i++) 6 { 7 Q[i] = Q[i-1]*2; 8 C[i][0] = 1; 9 for (int j = 1; j < 110; j++) 10 C[i][j] = C[i - 1][j] + C[i - 1][j - 1]; 11 } 12 }
1.12 求逆元
1 lint ex_gcd(lint a,lint b,lint &x,lint &y)//扩展欧几里得(扩展gcd) 2 { 3 if (a==0&&b==0) return -1; 4 if (b==0){x=1;y=0;return a;} 5 lint d=ex_gcd(b,a%b,y,x); 6 y-=a/b*x; 7 return d; 8 } 9 10 lint mod_inverse(lint a,lint n)//乘法逆元 11 { 12 lint x,y; 13 lint d = ex_gcd(a,n,x,y); 14 return (x%n+n)%n; 15 }
1 int find(int x) 2 { 3 int k=mod-2,ans=1; 4 while(k) 5 { 6 if (k&1) ans=(lint)ans*x%mod; 7 x=(lint)x*x%mod; 8 k>>=1; 9 } 10 return ans; 11 } x在%mod下的逆元
二.素数
2.1 素数筛
1 const int MAX = 100; 2 //快速素数筛,只筛选小于等于素数i的素数与i的乘积,既不会造成重复筛选,又不会遗漏。时间复杂度几乎是线性的。 3 //模板来源https://blog.csdn.net/stack_queue/article/details/53560887 4 long long su[MAX],cnt; 5 bool isprime[MAX]; 6 void prime() 7 { 8 cnt=1; 9 memset(isprime,1,sizeof(isprime));//初始化认为所有数都为素数 10 isprime[0]=isprime[1]=0;//0和1不是素数 11 for(long long i=2;i<=MAX;i++) 12 { 13 if(isprime[i]) 14 su[cnt++]=i;//保存素数i 15 for(long long j=1;j<cnt&&su[j]*i<MAX;j++) 16 { 17 isprime[su[j]*i]=0;//筛掉小于等于i的素数和i的积构成的合数 18 } 19 } 20 } 21 int main() 22 { 23 prime(); 24 for(long long i=1;i<cnt;i++) 25 printf("%d ",su[i]); 26 return 0; 27 }
2.2 米勒罗宾素数测试
1 //https://blog.csdn.net/u013654696/article/details/40056179 2 // 18位素数:154590409516822759 3 // 19位素数:2305843009213693951 (梅森素数) 4 // 19位素数:4384957924686954497 5 LL prime[6] = {2, 3, 5, 233, 331}; 6 LL qmul(LL x, LL y, LL mod) { // 乘法防止溢出, 如果p * p不爆LL的话可以直接乘; O(1)乘法或者转化成二进制加法 7 8 9 return (x * y - (long long)(x / (long double)mod * y + 1e-3) *mod + mod) % mod; 10 /* 11 LL ret = 0; 12 while(y) { 13 if(y & 1) 14 ret = (ret + x) % mod; 15 x = x * 2 % mod; 16 y >>= 1; 17 } 18 return ret; 19 */ 20 } 21 LL qpow(LL a, LL n, LL mod) { 22 LL ret = 1; 23 while(n) { 24 if(n & 1) ret = qmul(ret, a, mod); 25 a = qmul(a, a, mod); 26 n >>= 1; 27 } 28 return ret; 29 } 30 bool Miller_Rabin(LL p) { 31 if(p < 2) return 0; 32 if(p != 2 && p % 2 == 0) return 0; 33 LL s = p - 1; 34 while(! (s & 1)) s >>= 1; 35 for(int i = 0; i < 5; ++i) { 36 if(p == prime[i]) return 1; 37 LL t = s, m = qpow(prime[i], s, p); 38 while(t != p - 1 && m != 1 && m != p - 1) { 39 m = qmul(m, m, p); 40 t <<= 1; 41 } 42 if(m != p - 1 && !(t & 1)) return 0; 43 } 44 return 1; 45 }
2.3 分解质因数
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 #include<time.h> 5 #include<iostream> 6 #include<algorithm> 7 #include <map> 8 using namespace std; 9 10 map<int,int> mp; 11 map<int,int>::iterator it; 12 //**************************************************************** 13 // Miller_Rabin 算法进行素数测试 14 //速度快,而且可以判断 <2^63的数 15 //**************************************************************** 16 const int S=100;//随机算法判定次数,S越大,判错概率越小 17 18 19 //计算 (a*b)%c. a,b都是long long的数,直接相乘可能溢出的 20 // a,b,c <2^63 21 long long mult_mod(long long a,long long b,long long c) 22 { 23 a%=c; 24 b%=c; 25 long long ret=0; 26 while(b) 27 { 28 if(b&1){ret+=a;ret%=c;} 29 a<<=1; 30 if(a>=c)a%=c; 31 b>>=1; 32 } 33 return ret; 34 } 35 36 37 38 //计算 x^n %c 39 long long pow_mod(long long x,long long n,long long mod)//x^n%c 40 { 41 if(n==1)return x%mod; 42 x%=mod; 43 long long tmp=x; 44 long long ret=1; 45 while(n) 46 { 47 if(n&1) ret=mult_mod(ret,tmp,mod); 48 tmp=mult_mod(tmp,tmp,mod); 49 n>>=1; 50 } 51 return ret; 52 } 53 54 55 56 57 58 //以a为基,n-1=x*2^t a^(n-1)=1(mod n) 验证n是不是合数 59 //一定是合数返回true,不一定返回false 60 bool check(long long a,long long n,long long x,long long t) 61 { 62 long long ret=pow_mod(a,x,n); 63 long long last=ret; 64 for(int i=1;i<=t;i++) 65 { 66 ret=mult_mod(ret,ret,n); 67 if(ret==1&&last!=1&&last!=n-1) return true;//合数 68 last=ret; 69 } 70 if(ret!=1) return true; 71 return false; 72 } 73 74 // Miller_Rabin()算法素数判定 75 //是素数返回true.(可能是伪素数,但概率极小) 76 //合数返回false; 77 78 bool Miller_Rabin(long long n) 79 { 80 if(n<2)return false; 81 if(n==2)return true; 82 if((n&1)==0) return false;//偶数 83 long long x=n-1; 84 long long t=0; 85 while((x&1)==0){x>>=1;t++;} 86 for(int i=0;i<S;i++) 87 { 88 long long a=rand()%(n-1)+1;//rand()需要stdlib.h头文件 89 if(check(a,n,x,t)) 90 return false;//合数 91 } 92 return true; 93 } 94 95 96 //************************************************ 97 //pollard_rho 算法进行质因数分解 98 //************************************************ 99 long long factor[10000];//质因数分解结果(刚返回时是无序的) 100 int tol;//质因数的个数。数组小标从0开始 101 102 long long gcd(long long a,long long b) 103 { 104 if(a==0)return 1;//??????? 105 if(a<0) return gcd(-a,b); 106 while(b) 107 { 108 long long t=a%b; 109 a=b; 110 b=t; 111 } 112 return a; 113 } 114 115 long long Pollard_rho(long long x,long long c) 116 { 117 long long i=1,k=2; 118 long long x0=rand()%x; 119 long long y=x0; 120 while(1) 121 { 122 i++; 123 x0=(mult_mod(x0,x0,x)+c)%x; 124 long long d=gcd(y-x0,x); 125 if(d!=1&&d!=x) return d; 126 if(y==x0) return x; 127 if(i==k){y=x0;k+=k;} 128 } 129 } 130 //对n进行素因子分解 131 void findfac(long long n) 132 { 133 if(Miller_Rabin(n))//素数 134 { 135 mp[n]++; 136 return; 137 } 138 long long p=n; 139 while(p>=n)p=Pollard_rho(p,rand()%(n-1)+1); 140 findfac(p); 141 findfac(n/p); 142 } 143 144 int main() 145 { 146 srand(time(NULL)); 147 long long n; 148 while(scanf("%lld",&n)!=EOF) 149 { 150 if(n==1) 151 { 152 printf("1\n"); 153 continue; 154 } 155 mp.clear(); 156 tol=0; 157 findfac(n); 158 long long sum=1; 159 for(it=mp.begin();it!=mp.end();it++){ 160 sum*=(1+it->second); 161 } 162 printf("%lld\n",sum); 163 } 164 return 0; 165 }
三.数学相关
3.1 扩展欧几里得算法
1 int exgcd(int a,int b,int &x,int &y) 2 { 3 if(b==0) 4 { 5 x=1; 6 y=0; 7 return a; 8 } 9 int r=exgcd(b,a%b,x,y); 10 int t=x; 11 x=y; 12 y=t-a/b*y; 13 return r; 14 }
3.2 向量基本计算方法
1 #include<cstdio> 2 #include<cstring> 3 #include<cctype> 4 #include<cmath> 5 #include<algorithm> 6 #define eps 1e-8 7 using namespace std; 8 9 struct Point 10 { 11 double x,y; 12 Point(){} 13 Point(double x,double y):x(x),y(y){} 14 double operator *(const Point B)const{ return x*B.y-y*B.x; } 15 Point operator -(const Point B)const{ return Point(x-B.x,y-B.y); } 16 Point operator +(const Point B)const{ return Point(x+B.x,y+B.y); } 17 Point operator *(const double B)const{ return Point(x*B,y*B); } 18 }A,B,C,D,L1,L2,P,Q,MA,MC; 19 20 int main() 21 { 22 int T;scanf("%d",&T); 23 for(;T--;) 24 { 25 scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&A.x,&A.y,&B.x,&B.y,&C.x,&C.y,&D.x,&D.y); 26 if(A.y==B.y || C.y==D.y){ printf("0.00\n");continue; } 27 if(A.y<B.y) swap(A,B); 28 if(C.y<D.y) swap(C,D); 29 double a=(C-A) * (D-A),b = (D-B) * (C-B); 30 if(a*b<-eps){ printf("0.00\n"); continue;} 31 P=(B-A) * (a / (a+b)) + A; 32 if(P.y>C.y || P.y<D.y){ printf("0.00\n");continue; } 33 if(A.y>C.y) 34 { 35 Q=B + (A-B) * ((C.x-B.x)/(A.x-B.x)); 36 if(Q.y>=C.y && A.y>=Q.y){ printf("0.00\n");continue;} 37 MA=B + (A-B) * ((C.y-B.y)/(A.y-B.y)); 38 printf("%.2lf\n",fabs((MA-P) * (C-P) / 2)); 39 } 40 else 41 { 42 Q=D + (C-D) * ((A.x-D.x)/(C.x-D.x)); 43 if(Q.y>=A.y && C.y>=Q.y){ printf("0.00\n");continue; } 44 MC=D+(C-D) * ((A.y-D.y) / (C.y-D.y)); 45 printf("%.2lf\n",fabs((MC-P) * (A-P) / 2) ); 46 } 47 } 48 }
1 //https://blog.csdn.net/qq_16657927/article/details/79942140 2 /* 3 |16/11/06ztx| 4 */ 5 6 struct node { 7 double x; // 横坐标 8 double y; // 纵坐标 9 }; 10 11 typedef node Vector; 12 13 Vector operator + (Vector A, Vector B) { return Vector(A.x + B.x, A.y + B.y); } 14 Vector operator - (Point A, Point B) { return Vector(A.x - B.y, A.y - B.y); } 15 Vector operator * (Vector A, double p) { return Vector(A.x*p, A.y*p); } 16 Vector operator / (Vector A, double p) { return Vector(A.x / p, A.y*p); } 17 18 double Dot(Vector A, Vector B) { return A.x*B.x + A.y*B.y; } // 向量点乘 19 double Length(Vector A) { return sqrt(Dot(A, A)); } // 向量模长 20 double Angle(Vector A, Vector B) { return acos(Dot(A, B) / Length(A) / Length(B)); } // 向量之间夹角 21 22 double Cross(Vector A, Vector B) { // 叉积计算 公式 23 return A.x*B.y - A.y*B.x; 24 } 25 26 Vector Rotate(Vector A, double rad) // 向量旋转 公式 { 27 return Vector(A.x*cos(rad) - A.y*sin(rad), A.x*sin(rad) + A.y*cos(rad)); 28 } 29 30 Point getLineIntersection(Point P, Vector v, Point Q, Vector w) { // 两直线交点t1 t2计算公式 31 Vector u = P - Q; 32 double t = Cross(w, u) / Cross(v, w); // 求得是横坐标 33 return P + v*t; // 返回一个点 34 }
3.3 求多边形面积
1 /* 2 |16/11/06ztx| 3 */ 4 5 node G[maxn]; 6 int n; 7 8 double Cross(node a, node b) { // 叉积计算 9 return a.x*b.y - a.y*b.x; 10 } 11 12 13 int main() 14 { 15 while (scanf("%d", &n) != EOF && n) { 16 for (int i = 0; i < n; i++) 17 scanf("%lf %lf", &G[i].x, &G[i].y); 18 double sum = 0; 19 G[n].x = G[0].x; 20 G[n].y = G[0].y; 21 for (int i = 0; i < n; i++) { 22 sum += Cross(G[i], G[i + 1]); 23 } 24 // 或者 25 //for (int i = 0; i < n; i++) { 26 //sum += fun(G[i], G[(i + 1)% n]); 27 //} 28 sum = sum / 2.0; 29 printf("%.1f\n", sum); 30 } 31 system("pause"); 32 return 0; 33 }
3.4 判断直线相交
1 /* 2 |16/11/06ztx| 3 */ 4 5 node P[35][105]; 6 7 double Cross_Prouct(node A,node B,node C) { // 计算BA叉乘CA 8 return (B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x); 9 } 10 bool Intersect(node A,node B,node C,node D) { // 通过叉乘判断线段是否相交; 11 if(min(A.x,B.x)<=max(C.x,D.x)&& // 快速排斥实验; 12 min(C.x,D.x)<=max(A.x,B.x)&& 13 min(A.y,B.y)<=max(C.y,D.y)&& 14 min(C.y,D.y)<=max(A.y,B.y)&& 15 Cross_Prouct(A,B,C)*Cross_Prouct(A,B,D)<0&& // 跨立实验; 16 Cross_Prouct(C,D,A)*Cross_Prouct(C,D,B)<0) // 叉乘异号表示在两侧; 17 return true; 18 else return false; 19 }
3.5 凸包
1 /* 2 向量叉积判断多边形凹凸 3 4 对于连续的三个点p0,p1,p2,另向量a=p1-p0,b=p2-p1若是凸多边形,那么b相对于a一定是向逆时针方向 5 6 旋转的。 7 8 判断两向量的旋转方向,可以使用向量的叉积 a×b = x1×y2 - x2×y1 9 10 a×b > 0 b在a的逆时针方向 11 a×b = 0 b平行于a(共线) 12 a×b < 0 b在a的顺时针方向 13 14 要注意的是,对于最后一个点pn,还要和起始的两个点p0,p1判断一次。 15 */ 16 17 18 #include <cstdio> 19 #include <cmath> 20 #include <cstdlib> 21 #include <algorithm> 22 #define eps 1e-8 23 #define MAXN 110 24 using namespace std; 25 struct Point 26 { 27 double x, y; 28 Point(){} 29 Point(double X, double Y){ 30 x = X; y = Y; 31 } 32 }; 33 Point P[MAXN]; 34 int dcmp(double x) 35 { 36 if(fabs(x) < eps) 37 return 0; 38 else 39 return x < 0 ? -1 : 1; 40 } 41 Point operator - (Point A, Point B){ 42 return Point(A.x-B.x, A.y-B.y); 43 } 44 Point operator + (Point A, Point B){ 45 return Point(A.x+B.x, A.y+B.y); 46 } 47 Point operator * (Point A, double p){ 48 return Point(A.x*p, A.y*p);; 49 } 50 double Cross(Point A, Point B){ 51 return A.x*B.y - A.y*B.x; 52 } 53 double Dot(Point A, Point B){ 54 return A.x*B.x + A.y*B.y; 55 } 56 double Dis(Point A, Point B){ 57 return sqrt((A.x-B.x)*(A.x-B.x) + (A.y-B.y)*(A.y-B.y)); 58 } 59 bool operator == (Point A, Point B){ 60 return dcmp(A.x-B.x) == 0 && dcmp(A.y-B.y) == 0; 61 } 62 bool cmp(Point A, Point B)//按极角升序排序,若角度相等距离小的在前面 63 { 64 double temp = Cross(A-P[0], B-P[0]); 65 if(dcmp(temp) > 0) return true; 66 if(dcmp(temp) == 0 && dcmp(Dis(P[0], A) - Dis(P[0], B)) < 0) return true; 67 return false; 68 } 69 int Stack[MAXN], top;//下标从0开始计数到top 70 void input(int n) 71 { 72 scanf("%lf%lf", &P[0].x, &P[0].y); 73 double xx = P[0].x, yy = P[0].y; 74 int id = 0;//找到y坐标最小的点,若有多个选择x坐标最小的记录下标 75 for(int i = 1; i < n; i++) 76 { 77 scanf("%lf%lf", &P[i].x, &P[i].y); 78 if(P[i].y < yy || (P[i].y == yy && P[i].x < xx)) 79 { 80 xx = P[i].x; 81 yy = P[i].y; 82 id = i; 83 } 84 } 85 Point T; 86 T = P[0]; P[0] = P[id]; P[id] = T; 87 sort(P+1, P+n, cmp);//极角排序 88 } 89 void Graham(int n) 90 { 91 if(n == 1){ top = 0; Stack[0] = 0;} 92 else if(n == 2) 93 { 94 top = 1; 95 Stack[0] = 0; 96 Stack[1] = 1; 97 } 98 else 99 { 100 for(int i = 0; i <= 1; i++) 101 Stack[i] = i; 102 top = 1; 103 for(int i = 2; i < n; i++) 104 { //如果和上一条边成左旋关系,压栈继续;反之一直弹栈直到和栈顶两点的边成左转关系,压栈继续。 105 while(top > 0 && dcmp(Cross(P[Stack[top]]-P[Stack[top-1]], P[i]-P[Stack[top-1]])) <= 0) top--; 106 top++; 107 Stack[top] = i; 108 } 109 } 110 } 111 int main() 112 { 113 int n; 114 input(n); 115 Graham(n); 116 return 0; 117 }
3.6 计算几何模板
1 #include<iostream> 2 #include<cmath> 3 #include<cstdio> 4 #include<algorithm> 5 #define INF 1E200 6 using namespace std; 7 const double PI = 3.14159265;//||acos(-1) 8 const double eps=0.0000000001; 9 struct Point //点 10 { 11 double x,y; 12 Point (double a=0,double b=0):x(a),y(b) {} 13 }; 14 struct Line_segment //线段 15 { 16 Point s,e; 17 Line_segment() {} 18 Line_segment(Point a,Point b):s(a),e(b) {} 19 }; 20 struct Line //直线 21 { 22 double A,B,C; 23 Line(double A=1,double B=-1,double C=0):A(A),B(B),C(C) {} 24 }; 25 inline double Max(double a,double b) 26 { 27 return a>b?a:b; 28 } 29 inline double Min(double a,double b) 30 { 31 return a<b?a:b; 32 } 33 34 //计算几何 开始! 35 double Dist(Point a,Point b) //1.两点之间距离 36 { 37 return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); 38 } 39 bool equal_Point(Point a,Point b) //2.判断两点是否重合 40 { 41 return fabs(a.x-b.x)<eps&&fabs(a.y-b.y)<eps; 42 } 43 double multiply(Point sp,Point ep,Point op) //3.叉积//要判断点在直线哪一侧时,sp,ep为线段或者直线上两点,op为判断的点 44 { 45 /****************************************************************************** 46 r=multiply(sp,ep,op),得到(sp-op) 和 ( ep-op)的叉积 47 r>0; ep在矢量op sp的逆时针方向//点在直线右边 48 r=0;op sp ep 三点共线; 49 r<0;ep在矢量op sp的顺时针方向//点在直线左边 50 *******************************************************************************/ 51 return((sp.x-op.x)*(ep.y-op.y)-(ep.x-op.x)*(sp.y-op.y)); 52 } 53 double dotmultiply(Point p1,Point p2,Point p0) //4.点积 54 { 55 /****************************************************************************** 56 r=dotmultiply(p1,p2,op),得到矢量(p1-op)和(p2-op)的点积,如果两个矢量都非零矢量 57 r<0:两矢量夹角为钝角; 58 r=0:两矢量夹角为直角; 59 r>0:两矢量夹角为锐角; 60 *******************************************************************************/ 61 return (p1.x-p0.x)*(p2.x-p0.x)+(p1.y-p0.y)*(p2.y-p0.y); 62 } 63 bool online(Line_segment l,Point p)//5.判断点p是否在线段l上 +3 64 { 65 /****************************************************************************** 66 判断点p是否在线段l上 67 条件:(p在线段l所在的直线上) && (点p在以线段l为对角线的矩形内) 68 *******************************************************************************/ 69 return( (multiply(l.e,p,l.s)==0) &&( ( (p.x-l.s.x)*(p.x-l.e.x)<=0 )&&( (p.y-l.s.y)*(p.y-l.e.y)<=0 ) ) ); 70 } 71 Point Rotate(Point o,double alpha,Point p) // 6.返回点p以点o为圆心逆时针旋转alpha(单位:弧度)后所在的位置 72 { 73 Point tp; 74 p.x-=o.x; 75 p.y-=o.y; 76 tp.x=p.x*cos(alpha)-p.y*sin(alpha)+o.x; 77 tp.y=p.y*cos(alpha)+p.x*sin(alpha)+o.y; 78 return tp; 79 } 80 double angle(Point o,Point s,Point e) //7.返回顶角在o点,起始边为os,终止边为oe的夹角(单位:弧度) 81 { 82 /****************************************************************************** 83 返回顶角在o点,起始边为os,终止边为oe的夹角(单位:弧度) 84 角度小于pi,返回正值 85 角度大于pi,返回负值 86 可以用于求线段之间的夹角 87 原理: 88 r = dotmultiply(s,e,o) / (dist(o,s)*dist(o,e)) 89 r'= multiply(s,e,o) 90 r >= 1 angle = 0; 91 r <= -1 angle = -PI 92 -1<r<1 && r'>0 angle = arccos(r) 93 -1<r<1 && r'<=0 angle = -arccos(r) 94 ********************************************************************************/ 95 double cosfi,fi,norm; 96 double dsx = s.x - o.x; 97 double dsy = s.y - o.y; 98 double dex = e.x - o.x; 99 double dey = e.y - o.y; 100 cosfi=dsx*dex+dsy*dey; 101 norm=(dsx*dsx+dsy*dsy)*(dex*dex+dey*dey); 102 cosfi /= sqrt( norm ); 103 if (cosfi >= 1.0 ) return 0; 104 if (cosfi <= -1.0 ) return -3.1415926; 105 fi=acos(cosfi); 106 if (dsx*dey-dsy*dex>0) return fi; // 说明矢量os 在矢量 oe的顺时针方向 107 return -fi; 108 } 109 double relation(Point p,Line_segment l) //8.判断点与线段的关系 110 { 111 /****************************************************************************** 112 判断点与线段的关系,用途很广泛 113 本函数是根据下面的公式写的,P是点C到线段AB所在直线的垂足 114 AC dot AB 115 r = --------- 116 ||AB||^2 117 (Cx-Ax)(Bx-Ax) + (Cy-Ay)(By-Ay) 118 = ------------------------------- 119 L^2 120 r has the following meaning: 121 r=0 P = A 122 r=1 P = B 123 r<0 P is on the backward extension of AB 124 r>1 P is on the forward extension of AB 125 0<r<1 P is interior to AB 126 ********************************************************************************/ 127 Line_segment tl; 128 tl.s=l.s; 129 tl.e=p; 130 return dotmultiply(tl.e,l.e,l.s)/(Dist(l.s,l.e)*Dist(l.s,l.e)); 131 } 132 Point perpendicular(Point p,Line_segment l) //9.求点C到线段AB所在直线的垂足 P 133 { 134 /****************************************************************************** 135 求点C到线段AB所在直线的垂足 P 136 *******************************************************************************/ 137 double r=relation(p,l); 138 Point tp; 139 tp.x=l.s.x+r*(l.e.x-l.s.x); 140 tp.y=l.s.y+r*(l.e.y-l.s.y); 141 return tp; 142 } 143 double ptolinesegdist(Point p,Line_segment l,Point &np) //10.求点p到线段l的最短距离,并返回线段上距该点最近的点np 144 { 145 /****************************************************************************** 146 求点p到线段l的最短距离,并返回线段上距该点最近的点np 147 注意:np是线段l上到点p最近的点,不一定是垂足 148 *******************************************************************************/ 149 double r=relation(p,l); 150 if(r<0) 151 { 152 np=l.s; 153 return Dist(p,l.s); 154 } 155 if(r>1) 156 { 157 np=l.e; 158 return Dist(p,l.e); 159 } 160 np=perpendicular(p,l); 161 return Dist(p,np); 162 } 163 double ptoldist(Point p,Line_segment l) // 11.求点p到线段l所在直线的距离,请注意本函数与上个函数的区别 164 { 165 return abs(multiply(p,l.e,l.s))/Dist(l.s,l.e); 166 } 167 double ptopointset(int vcount,Point pointset[],Point p,Point &q) //12.计算点到折线集的最近距离,并返回最近点. 168 { 169 /****************************************************************************** 170 计算点到折线集的最近距离,并返回最近点. 171 注意:调用的是ptolineseg()函数 172 *******************************************************************************/ 173 int i; 174 double cd=double(INF),td; 175 Line_segment l; 176 Point tq,cq; 177 for(i=0; i<vcount-1; i++) 178 { 179 l.s=pointset[i]; 180 l.e=pointset[i+1]; 181 td=ptolinesegdist(p,l,tq); 182 if(td<cd) 183 { 184 cd=td; 185 cq=tq; 186 } 187 } 188 q=cq; 189 return cd; 190 } 191 bool CircleInsidePolygon(int vcount,Point center,double radius,Point polygon[]) //13.判断圆是否在多边形内.ptolineseg()函数的应用 192 { 193 Point q; 194 double d; 195 q.x=0; 196 q.y=0; 197 d=ptopointset(vcount,polygon,center,q); 198 if(d>radius||fabs(d-radius)<eps) 199 return true;//若不考虑相切的情况,去掉 fabs(d-radius)<eps 200 201 else 202 return false; 203 } 204 double cosine(Line_segment l1,Line_segment l2) //14.返回两个 矢量 l1和l2的夹角的余弦 205 { 206 /****************************************************************************** 207 返回两个矢量l1和l2的夹角的余弦(-1 --- 1)注意:如果想从余弦求夹角的话,注意反余弦函数的定义域是从 0到pi 208 *******************************************************************************/ 209 return (((l1.e.x-l1.s.x)*(l2.e.x-l2.s.x) +(l1.e.y-l1.s.y)*(l2.e.y-l2.s.y))/(Dist(l1.e,l1.s)*Dist(l2.e,l2.s))); 210 } 211 double lsangle(Line_segment l1,Line_segment l2) // 15.返回线段l1与l2之间的夹角 单位:弧度 范围(-pi,pi) 212 { 213 Point o,s,e; 214 o.x=o.y=0; 215 s.x=l1.e.x-l1.s.x; 216 s.y=l1.e.y-l1.s.y; 217 e.x=l2.e.x-l2.s.x; 218 e.y=l2.e.y-l2.s.y; 219 return angle(o,s,e); 220 } 221 bool intersect(Line_segment u,Line_segment v) // 16.如果线段u和v相交(包括相交在端点处)时,返回true 222 { 223 /****************************************************************************** 224 如果线段u和v相交(包括相交在端点处)时,返回true 225 判断P1P2跨立Q1Q2的依据是: ( P1 - Q1 ) x ( Q2 - Q1 ) x ( Q2 - Q1 ) x ( P2 - Q1 ) >= 0 226 判断Q1Q2跨立P1P2的依据是: ( Q1 - P1 ) x ( P2 - P1 ) x ( P2 - P1 ) x ( Q2 - P1 ) >= 0 227 *******************************************************************************/ 228 return((Max(u.s.x,u.e.x)>=Min(v.s.x,v.e.x))&& //排斥实验 229 (Max(v.s.x,v.e.x)>=Min(u.s.x,u.e.x))&& 230 (Max(u.s.y,u.e.y)>=Min(v.s.y,v.e.y))&& 231 (Max(v.s.y,v.e.y)>=Min(u.s.y,u.e.y))&& 232 (multiply(v.s,u.e,u.s)*multiply(u.e,v.e,u.s)>=0)&& //跨立实验 233 (multiply(u.s,v.e,v.s)*multiply(v.e,u.e,v.s)>=0)); 234 } 235 bool intersect_A(Line_segment u,Line_segment v) // 17.(线段u和v相交)&&(交点不是端点)时返回true 236 { 237 return ((intersect(u,v))&& 238 (!online(u,v.s))&& 239 (!online(u,v.e))&& 240 (!online(v,u.e))&& 241 (!online(v,u.s))); 242 } 243 bool intersect_l(Line_segment u,Line_segment v)// 18.线段v所在直线与线段u相交时返回true 244 { 245 /****************************************************************************** 246 线段v所在直线与线段u相交时返回true;方法;判断线段u是否跨立线段v 247 *******************************************************************************/ 248 return multiply(u.s,v.e,v.s)*multiply(v.e,u.e,v.s)>=0; 249 } 250 Line makeline(Point p1,Point p2) // 19.根据已知两点坐标,求过这两点的直线解析方程 251 { 252 /****************************************************************************** 253 根据已知两点坐标,求过这两点的直线解析方程Ax+By+C=0 (A>=0) 254 *******************************************************************************/ 255 Line tl; 256 int sign = 1; 257 tl.A=p2.y-p1.y; 258 if(tl.A<0) 259 { 260 sign = -1; 261 tl.A=sign*tl.A; 262 } 263 tl.B=sign*(p1.x-p2.x); 264 tl.C=sign*(p1.y*p2.x-p1.x*p2.y); 265 return tl; 266 } 267 double slope(Line l) // 20.根据直线解析方程返回直线的斜率k, 水平线返回 0, 竖直线返回 1e200 268 { 269 if(abs(l.A) < 1e-20) 270 return 0; 271 if(abs(l.B) < 1e-20) 272 return INF; 273 return -(l.A/l.B); 274 } 275 double alpha(Line l) //21. 返回直线的倾斜角 alpha ( 0 - pi) 276 { 277 if(abs(l.A)< eps) 278 return 0; 279 if(abs(l.B)< eps) 280 return PI/2; 281 double k=slope(l); 282 if(k>0) 283 return atan(k); 284 else 285 return PI+atan(k); 286 } 287 Point symmetry(Line l,Point p) //22. 求点p关于直线l的对称点 288 { 289 Point tp; 290 tp.x=((l.B*l.B-l.A*l.A)*p.x-2*l.A*l.B*p.y-2*l.A*l.C)/(l.A*l.A+l.B*l.B); 291 tp.y=((l.A*l.A-l.B*l.B)*p.y-2*l.A*l.B*p.x-2*l.B*l.C)/(l.A*l.A+l.B*l.B); 292 return tp; 293 } 294 bool lineintersect(Line l1,Line l2,Point &p) // 23.两直线相交返回true并返回交点p,不相交则返回false 295 { 296 double d=l1.A*l2.B-l2.A*l1.B; 297 if(abs(d)<eps) // 不相交 298 return false; 299 p.x = (l2.C*l1.B-l1.C*l2.B)/d; 300 p.y = (l2.A*l1.C-l1.A*l2.C)/d; 301 return true; 302 } 303 bool intersection(Line_segment l1,Line_segment l2,Point &inter) // 24.如果线段l1和l2相交,返回true且交点由(inter)返回,否则返回false 304 { 305 Line ll1,ll2; 306 ll1=makeline(l1.s,l1.e); 307 ll2=makeline(l2.s,l2.e); 308 if(intersect(l1,l2)==1) 309 { 310 lineintersect(ll1,ll2,inter); 311 return true; 312 } 313 else 314 return false; 315 } 316 /*******************************************************************************/ 317 bool point_in_circle(Point o,double r,Point p) //25. 返回值:点p在圆内(包括边界)时,返回true 318 { 319 /****************************************************************************** 320 参数o为圆心,r为半径,p为判断的点 321 返回值:点p在圆内(包括边界)时,返回true 322 *******************************************************************************/ 323 double d2=(p.x-o.x)*(p.x-o.x)+(p.y-o.y)*(p.y-o.y); 324 double r2=r*r; 325 return d2<r2||abs(d2-r2)<eps; 326 } 327 328 bool cocircle(Point p1,Point p2,Point p3,Point &q,double &r) //26.三点确定一个圆,不能构成圆返回false 329 { 330 /****************************************************************************** 331 用 途 :求不共线的三点确定一个圆 332 输 入 :三个点p1,p2,p3 333 返回值 :如果三点共线,返回false;反之,返回true。圆心由q返回,半径由r返回 334 *******************************************************************************/ 335 double x12=p2.x-p1.x; 336 double y12=p2.y-p1.y; 337 double x13=p3.x-p1.x; 338 double y13=p3.y-p1.y; 339 double z2=x12*(p1.x+p2.x)+y12*(p1.y+p2.y); 340 double z3=x13*(p1.x+p3.x)+y13*(p1.y+p3.y); 341 double d=2.0*(x12*(p3.y-p2.y)-y12*(p3.x-p2.x)); 342 if(abs(d)<eps) //共线,圆不存在 343 return false; 344 q.x=(y13*z2-y12*z3)/d; 345 q.y=(x12*z3-x13*z2)/d; 346 r=Dist(p1,q); 347 return true; 348 } 349 int CircleRelation(Point p1, double r1, Point p2, double r2) //27.两圆位置关系 350 { 351 /****************************************************************************** 352 相离:return 1 353 外切:return 2 354 相交:return 3 355 内切:return 4 356 内含:return 5 357 *******************************************************************************/ 358 double d = sqrt( (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y) ); 359 if( fabs(d-r1-r2) < eps ) // 必须保证前两个if先被判定! 360 return 2; 361 if( fabs(d-fabs(r1-r2)) < eps ) 362 return 4; 363 if( d > r1+r2 ) 364 return 1; 365 if( d < fabs(r1-r2) ) 366 return 5; 367 if( fabs(r1-r2) < d && d < r1+r2 ) 368 return 3; 369 return 0; // indicate an error!未知错误 370 } 371 double P2planeDist(double x, double y, double z, double a, double b, double c, double d) //28.空间 点到平面距离 372 { 373 /****************************************************************************** 374 空间点到平面的距离,平面用一般式表示ax+by+cz+d=0 375 *******************************************************************************/ 376 return fabs(a*x+b*y+c*z+d) / sqrt(a*a+b*b+c*c); 377 } 378 bool SameSide(Point p1, Point p2, Line line) //29.点在直线同侧返回true 379 { 380 return (line.A * p1.x + line.B * p1.y + line.C) * 381 (line.A * p2.x + line.B * p2.y + line.C) > 0; 382 } 383 void c2point(Point p1,double r1,Point p2,double r2,Point &rp1,Point &rp2) //30.两个圆(已判断为相交或相切)的交点rp1,rp2 384 { 385 double a,b,r; 386 a=p2.x-p1.x; 387 b=p2.y-p1.y; 388 r=(a*a+b*b+r1*r1-r2*r2)/2; 389 if(a==0&&b!=0) 390 { 391 rp1.y=rp2.y=r/b; 392 rp1.x=sqrt(r1*r1-rp1.y*rp1.y); 393 rp2.x=-rp1.x; 394 } 395 else if(a!=0&&b==0) 396 { 397 rp1.x=rp2.x=r/a; 398 rp1.y=sqrt(r1*r1-rp1.x*rp2.x); 399 rp2.y=-rp1.y; 400 } 401 else if(a!=0&&b!=0) 402 { 403 double delta; 404 delta=b*b*r*r-(a*a+b*b)*(r*r-r1*r1*a*a); 405 rp1.y=(b*r+sqrt(delta))/(a*a+b*b); 406 rp2.y=(b*r-sqrt(delta))/(a*a+b*b); 407 rp1.x=(r-b*rp1.y)/a; 408 rp2.x=(r-b*rp2.y)/a; 409 } 410 rp1.x+=p1.x; 411 rp1.y+=p1.y; 412 rp2.x+=p1.x; 413 rp2.y+=p1.y; 414 } 415 double c2area(Point p1,double r1,Point p2,double r2) //31.两相交圆公共面积 +30 416 { 417 double TEMP; 418 Point rp1,rp2,rp; 419 c2point(p1,r1,p2,r2,rp1,rp2); 420 if(r1>r2) //保证r2>r1 421 { 422 rp=p1; 423 p1=p2; 424 p2=rp; 425 TEMP=r1; 426 r1=r2; 427 r2=TEMP; 428 } 429 double a,b,rr; 430 a=p1.x-p2.x; 431 b=p1.y-p2.y; 432 rr=sqrt(a*a+b*b); 433 double dx1,dy1,dx2,dy2; 434 double sita1,sita2; 435 dx1=rp1.x-p1.x; 436 dy1=rp1.y-p1.y; 437 dx2=rp2.x-p1.x; 438 dy2=rp2.y-p1.y; 439 sita1=acos((dx1*dx2+dy1*dy2)/r1/r1); 440 dx1=rp1.x-p2.x; 441 dy1=rp1.y-p2.y; 442 dx2=rp2.x-p2.x; 443 dy2=rp2.y-p2.y; 444 sita2=acos((dx1*dx2+dy1*dy2)/r2/r2); 445 double s=0; 446 if(rr<r2)//相交弧为优弧 447 s=r1*r1*(PI-sita1/2+sin(sita1)/2)+r2*r2*(sita2-sin(sita2))/2; 448 else//相交弧为劣弧 449 s=(r1*r1*(sita1-sin(sita1))+r2*r2*(sita2-sin(sita2)))/2; 450 451 452 return s; 453 } 454 455 int clpoint(Point p,double r,double a,double b,double c,Point &rp1,Point &rp2) //32.圆和直线(ax+by+c=0,a>=0)关系 456 { 457 /****************************************************************************** 458 相离 return 0 459 相切 return 1 460 相交 return 2 461 *******************************************************************************/ 462 int res=0; 463 c=c+a*p.x+b*p.y; 464 double tmp; 465 if(a==0&&b!=0) 466 { 467 tmp=-c/b; 468 if(r*r<tmp*tmp) 469 res=0; 470 else if(r*r==tmp*tmp) 471 { 472 res=1; 473 rp1.y=tmp; 474 rp1.x=0; 475 } 476 else 477 { 478 res=2; 479 rp1.y=rp2.y=tmp; 480 rp1.x=sqrt(r*r-tmp*tmp); 481 rp2.x=-rp1.x; 482 } 483 } 484 else if(a!=0&&b==0) 485 { 486 tmp=-c/a; 487 if(r*r<tmp*tmp) 488 res=0; 489 else if(r*r==tmp*tmp) 490 { 491 res=1; 492 rp1.x=tmp; 493 rp1.y=0; 494 } 495 else 496 { 497 res=2; 498 rp1.x=rp2.x=tmp; 499 rp1.y=sqrt(r*r-tmp*tmp); 500 rp2.y=-rp1.y; 501 } 502 } 503 else if(a!=0&&b!=0) 504 { 505 double delta; 506 delta=b*b*c*c-(a*a+b*b)*(c*c-a*a*r*r); 507 if(delta<0) 508 res=0; 509 else if(delta==0) 510 { 511 res=1; 512 rp1.y=-b*c/(a*a+b*b); 513 rp1.x=(-c-b*rp1.y)/a; 514 } 515 else 516 { 517 res=2; 518 rp1.y=(-b*c+sqrt(delta))/(a*a+b*b); 519 rp2.y=(-b*c-sqrt(delta))/(a*a+b*b); 520 rp1.x=(-c-b*rp1.y)/a; 521 rp2.x=(-c-b*rp2.y)/a; 522 } 523 } 524 rp1.x+=p.x; 525 rp1.y+=p.y; 526 rp2.x+=p.x; 527 rp2.y+=p.y; 528 return res; 529 } 530 void incircle(Point p1,Point p2,Point p3,Point &rp,double &r) // 33.三角形内切圆 531 { 532 double dx31,dy31,dx21,dy21,d31,d21,a1,b1,c1; 533 dx31=p3.x-p1.x; 534 dy31=p3.y-p1.y; 535 dx21=p2.x-p1.x; 536 dy21=p2.y-p1.y; 537 d31=sqrt(dx31*dx31+dy31*dy31); 538 d21=sqrt(dx21*dx21+dy21*dy21); 539 a1=dx31*d21-dx21*d31; 540 b1=dy31*d21-dy21*d31; 541 c1=a1*p1.x+b1*p1.y; 542 double dx32,dy32,dx12,dy12,d32,d12,a2,b2,c2; 543 dx32=p3.x-p2.x; 544 dy32=p3.y-p2.y; 545 dx12=-dx21; 546 dy12=-dy21; 547 d32=sqrt(dx32*dx32+dy32*dy32); 548 d12=d21; 549 a2=dx12*d32-dx32*d12; 550 b2=dy12*d32-dy32*d12; 551 c2=a2*p2.x+b2*p2.y; 552 rp.x=(c1*b2-c2*b1)/(a1*b2-a2*b1); 553 rp.y=(c2*a1-c1*a2)/(a1*b2-a2*b1); 554 r=fabs(dy21*rp.x-dx21*rp.y+dx21*p1.y-dy21*p1.x)/d21; 555 } 556 557 void cutpoint(Point p,double r,Point sp,Point &rp1,Point &rp2) //34.过圆外一点的直线与圆的两个切点(p为圆心,r为圆半径,点sp为圆外一点) 558 { 559 Point p2; 560 p2.x=(p.x+sp.x)/2; 561 p2.y=(p.y+sp.y)/2; 562 double dx2,dy2,r2; 563 dx2=p2.x-p.x; 564 dy2=p2.y-p.y; 565 r2=sqrt(dx2*dx2+dy2*dy2); 566 c2point(p,r,p2,r2,rp1,rp2); 567 } 568 569 void DoneSq(Point a, Point c,Point& b,Point &d)//35.已知正方形对角线上两顶点(a和c),求另两点(b和d); 570 { 571 double x,y,mx,my; 572 mx = (a.x+c.x)/2.0; 573 my = (a.y+c.y)/2.0; 574 x = a.x - mx; 575 y = a.y - my; 576 b.x = -y + mx; 577 b.y = x + my; 578 x = c.x - mx; 579 y = c.y - my; 580 d.x = - y + mx; 581 d.y = x + my; 582 } 583 void makeline2(Line& L,const Point& a,const double& k)// 36. 根据一点坐标及其斜率k(已证实存在),求这条直线的解析方程 584 { 585 L.A=k; 586 L.B=-1; 587 L.C=a.y-k*a.x; 588 } 589 590 void min_cover_circle(Point *p,int n,Point &c,double &r)// 37.随机增量算法求最小覆盖圆 (点/点的个数/圆心/半径) 591 { 592 random_shuffle(p,p+n); 593 c=p[0]; 594 r=0; 595 for(int i=1; i<n; i++) 596 { 597 if(Dist(p[i],c)>r+eps) 598 { 599 c=p[i]; 600 r=0; 601 for(int j=0; j<i; j++) 602 if(Dist(p[j],c)>r+eps) 603 { 604 c.x=(p[i].x+p[j].x)/2; 605 c.y=(p[i].y+p[j].y)/2; 606 r=Dist(p[j],c); 607 for(int k=0; k<j; k++) 608 if(Dist(p[k],c)>r+eps) 609 { 610 cocircle(p[i],p[j],p[k],c,r); 611 } 612 } 613 } 614 } 615 } 616 bool InsideConvexPolygon(int vcount,Point polygon[],Point q) // 38.判断点q是否在凸多边形内//多边形顶点<=2时返回0 617 { 618 if(vcount<3) return 0; 619 Point p; 620 Line_segment l; 621 int i; 622 p.x=0; 623 p.y=0; 624 for(i=0; i<vcount; i++) // 寻找一个肯定在多边形polygon内的点p;多边形顶点平均值 625 { 626 p.x+=polygon[i].x; 627 p.y+=polygon[i].y; 628 } 629 p.x /= vcount; 630 p.y /= vcount; 631 632 for(i=0; i<vcount; i++) 633 { 634 l.s=polygon[i]; 635 l.e=polygon[(i+1)%vcount]; 636 if(multiply(p,l.e,l.s)*multiply(q,l.e,l.s)<0) /* 点p和点q在边l的两侧,说明点q肯定在多边形外 */ 637 break; 638 } 639 return (i==vcount); 640 } 641 642 double Getarea(int n,Point top[])// 39.计算顶点已按逆时针或顺时针排好的 多边形面积, 643 { 644 Point p=top[0]; 645 double s=0; 646 for(int i=0; i<n-1; i++) 647 { 648 s+=top[i].x*top[i+1].y-top[i].y*top[i+1].x; 649 } 650 s+=top[n-1].x*top[0].y-top[n-1].y*top[0].x; 651 return fabs(s/2); 652 } 653 bool cmp1(Point a,Point b)//凸包排序方法 654 { 655 if(a.x == b.x) 656 return a.y < b.y; 657 return a.x < b.x; 658 } 659 660 int graham(Point pnt[],int n,Point res[])//40.求凸包,pnt为点集,n为点的个数,res为凸包上的点,返回值为凸包点的个数 661 { 662 sort(pnt,pnt+n,cmp1); 663 int m=0, i, k; 664 for(i = 0; i < n; i++) 665 { 666 while(m>1 && multiply(res[m-1],pnt[i],res[m-2])<=0) 667 m--; 668 res[m++]=pnt[i]; 669 } 670 k = m; 671 for(i = n-2; i >= 0; i--) 672 { 673 while(m>k && multiply(res[m-1],pnt[i],res[m-2])<=0) 674 m--; 675 res[m++]=pnt[i]; 676 } 677 if(n > 1)//起始点重复。 678 m--; 679 return m; 680 } 681 double area1(Point c,double r,int n)//41.圆与n边形相交面积 c为圆心,r为半径 数组P为含n个点的数组 682 { 683 Point a,b; 684 double A,B,C,x,y,tS,Ans=0; 685 for(int i=0; i<n; i++) 686 { 687 a=P[i]; //数组P为多边形顶点数组 688 b=P[(i+1)%n]; 689 A=Dist(b,c); 690 B=Dist(a,c); 691 C=Dist(b,a); 692 if(A<r&&B<r) 693 Ans+= multiply(a,b,c)/2; 694 else if(A<r&&B>=r) 695 { 696 x=(dotmultiply(a,c,b)+sqrt(r*r*C*C-multiply(a,c,b)*multiply(a,c,b)))/C; 697 tS=multiply(a,b,c)/2; 698 Ans+= asin(tS*(1-x/C)*2/r/B*(1-eps))*r*r/2+tS*x/C; 699 } 700 else if(A>=r&&B<r) 701 { 702 y=(dotmultiply(b,c,a)+sqrt(r*r*C*C-multiply(b,c,a)*multiply(b,c,a)))/C; 703 tS=multiply(a,b,c)/2; 704 Ans+= asin(tS*(1-y/C)*2/r/A*(1-eps))*r*r/2+tS*y/C; 705 } 706 else if(fabs(multiply(a,b,c))>=r*C||dotmultiply(b,c,a)<=0||dotmultiply(a,c,b)<=0) 707 { 708 if(dotmultiply(a,b,c)<0) 709 if(multiply(a,b,c)<0) 710 Ans+= (-acos(-1.0)-asin(multiply(a,b,c)/A/B*(1-eps)))*r*r/2; 711 else Ans+= (acos(-1.0)-asin(multiply(a,b,c)/A/B*(1-eps)))*r*r/2; 712 else Ans+= asin(multiply(a,b,c)/A/B*(1-eps))*r*r/2; 713 } 714 else 715 { 716 x=(dotmultiply(a,c,b)+sqrt(r*r*C*C-multiply(a,c,b)*multiply(a,c,b)))/C; 717 y=(dotmultiply(b,c,a)+sqrt(r*r*C*C-multiply(b,c,a)*multiply(b,c,a)))/C; 718 tS=multiply(a,b,c)/2; 719 Ans+= (asin(tS*(1-x/C)*2/r/B*(1-eps))+asin(tS*(1-y/C)*2/r/A*(1-eps)))*r*r/2+tS*((y+x)/C-1); 720 } 721 } 722 return Ans; 723 } 724 int main() 725 { 726 double a,b,c,d; 727 Point m(1,1),n(2,2); 728 Line_segment w(m,n); 729 while(~scanf("%lf %lf",&a,&b)) 730 { 731 Point A(a,b); 732 if(online(w,A)) printf("yes\n"); 733 else printf("n0\n"); 734 } 735 return 0; 736 }
3.7 孙子定理 中国剩余定理
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 typedef long long ll; 8 ll a[100005],r[100005]; 9 int n; 10 11 ll exgcd(ll a,ll b,ll &x,ll &y){ 12 if(b==0) return x=1,y=0,a; 13 ll tmp=exgcd(b,a%b,y,x); 14 y-=a/b*x; 15 return tmp; 16 } 17 18 ll solve(){ 19 ll M=a[1],R=r[1],x,y,d; 20 for(int i=2;i<=n;i++){ 21 d=exgcd(M,a[i],x,y); 22 if((R-r[i])%d!=0) return -1; 23 x=(R-r[i])/d*x%a[i]; 24 R-=x*M; 25 M=M/d*a[i]; 26 R%=M; 27 } 28 return (R%M+M)%M; 29 } 30 31 int main(){ 32 while(~scanf("%d",&n)){ 33 for(int i=1;i<=n;i++)scanf("%lld%lld",&a[i],&r[i]); 34 printf("%lld\n",solve()); 35 } 36 return 0; 37 }
3.8 辛普森积分
1 double simpson(double a,double b) 2 { 3 double c = a + (b-a)/2; 4 return (F(a) + 4*F(c) + F(b))*(b-a)/6; 5 } 6 double asr(double a,double b,double eps,double A) 7 { 8 double c = a + (b-a)/2; 9 double L = simpson(a,c), R = simpson(c,b); 10 if(fabs(L + R - A) <= 15*eps)return L + R + (L + R - A)/15.0; 11 return asr(a,c,eps/2,L) + asr(c,b,eps/2,R); 12 } 13 double asr(double a,double b,double eps) 14 { 15 return asr(a,b,eps,simpson(a,b)); 16 }
3.9 高斯消元求解线性方程组
1 #include<stdio.h> 2 #include<algorithm> 3 #include<iostream> 4 #include<string.h> 5 #include<math.h> 6 using namespace std; 7 const int MAXN=50; 8 int a[MAXN][MAXN];//增广矩阵 9 int x[MAXN];//解集 10 bool free_x[MAXN];//标记是否是不确定的变元 11 int gcd(int a,int b){ 12 if(b == 0) return a; else return gcd(b,a%b); 13 } 14 inline int lcm(int a,int b){ 15 return a/gcd(a,b)*b;//先除后乘防溢出 16 } 17 // 高斯消元法解方程组(Gauss-Jordan elimination).(-2表示有浮点数解,但无整数解, 18 //-1表示无解,0表示唯一解,大于0表示无穷解,并返回自由变元的个数) 19 //有equ个方程,var个变元。增广矩阵行数为equ,分别为0到equ-1,列数为var+1,分别为0到var. 20 int Gauss(int equ,int var){ 21 int i,j,k; 22 int max_r;// 当前这列绝对值最大的行. 23 int col;//当前处理的列 24 int ta,tb; 25 int LCM; 26 int temp; 27 int free_x_num; 28 int free_index; 29 30 for(int i=0;i<=var;i++){ 31 x[i]=0; 32 free_x[i]=true; 33 } 34 35 //转换为阶梯阵. 36 col=0; // 当前处理的列 37 for(k = 0;k < equ && col < var;k++,col++){// 枚举当前处理的行. 38 // 找到该col列元素绝对值最大的那行与第k行交换.(为了在除法时减小误差) 39 max_r=k; 40 for(i=k+1;i<equ;i++){ 41 if(abs(a[i][col])>abs(a[max_r][col])) max_r=i; 42 } 43 if(max_r!=k){// 与第k行交换. 44 for(j=k;j<var+1;j++) swap(a[k][j],a[max_r][j]); 45 } 46 if(a[k][col]==0){// 说明该col列第k行以下全是0了,则处理当前行的下一列. 47 k--; 48 continue; 49 } 50 for(i=k+1;i<equ;i++){// 枚举要删去的行. 51 if(a[i][col]!=0){ 52 LCM = lcm(abs(a[i][col]),abs(a[k][col])); 53 ta = LCM/abs(a[i][col]); 54 tb = LCM/abs(a[k][col]); 55 if(a[i][col]*a[k][col]<0)tb=-tb;//异号的情况是相加 56 for(j=col;j<var+1;j++){ 57 a[i][j] = a[i][j]*ta-a[k][j]*tb; 58 } 59 } 60 } 61 } 62 // 1. 无解的情况: 化简的增广阵中存在(0, 0, ..., a)这样的行(a != 0). 63 for (i = k; i < equ; i++){ // 对于无穷解来说,如果要判断哪些是自由变元,那么初等行变换中的交换就会影响,则要记录交换. 64 if (a[i][col] != 0) return -1; 65 } 66 // 2. 无穷解的情况: 在var * (var + 1)的增广阵中出现(0, 0, ..., 0)这样的行,即说明没有形成严格的上三角阵. 67 // 且出现的行数即为自由变元的个数. 68 if (k < var){ 69 return var - k; // 自由变元有var - k个. 70 } 71 // 3. 唯一解的情况: 在var * (var + 1)的增广阵中形成严格的上三角阵. 72 // 计算出Xn-1, Xn-2 ... X0. 73 for (i = var - 1; i >= 0; i--){ 74 temp = a[i][var]; 75 for (j = i + 1; j < var; j++){ 76 if (a[i][j] != 0) temp -= a[i][j] * x[j]; 77 } 78 if (temp % a[i][i] != 0) return -2; // 说明有浮点数解,但无整数解. 79 x[i] = temp / a[i][i]; 80 } 81 return 0; 82 } 83 int main(void){ 84 // freopen("in.txt", "r", stdin); 85 // freopen("out.txt","w",stdout); 86 int i, j; 87 int equ,var; 88 while (scanf("%d %d", &equ, &var) != EOF){ 89 memset(a, 0, sizeof(a)); 90 for (i = 0; i < equ; i++){ 91 for (j = 0; j < var + 1; j++){ 92 scanf("%d", &a[i][j]); 93 } 94 } 95 int free_num = Gauss(equ,var); 96 if (free_num == -1) printf("无解!\n"); 97 else if (free_num == -2) printf("有浮点数解,无整数解!\n"); 98 else if (free_num > 0){ 99 printf("无穷多解! 自由变元个数为%d\n", free_num); 100 for (i = 0; i < var; i++){ 101 if (free_x[i]) printf("x%d 是不确定的\n", i + 1); 102 else printf("x%d: %d\n", i + 1, x[i]); 103 } 104 }else{ 105 for (i = 0; i < var; i++){ 106 printf("x%d: %d\n", i + 1, x[i]); 107 } 108 } 109 printf("\n"); 110 } 111 return 0; 112 }
3.10 整除分块
1 for(int l=1,r;l<=n;l=r+1) 2 { 3 r=n/(n/l); 4 ans+=(r-l+1)*(n/l); 5 }
3.11 求莫比乌斯函数
1 /*莫比乌斯函数具有两个性质: 2 1.对于任意正整数nn,∑d|nμ(d)=[n=1]∑d|nμ(d)=[n=1]。([n=1][n=1]表示只有当n=1n=1成立时,返回值为11;否则,值为00;(这个就是用μμ是容斥系数的性质可以证明)(PS:这一条性质是莫比乌斯反演中最常用的) 3 所有可以整除n的数的莫比乌斯函数之和等于0(特别的,n==1时等于1)。 4 2.对于任意正整数nn,∑d|nμ(d)d=ϕ(n)n∑d|nμ(d)d=ϕ(n)n。(这个性质很奇妙,它把欧拉函数和莫比乌斯函数结合起来,或许我之后写杜教筛的学习笔记时会去证明吧) 5 所有可以整除n的数d的莫比乌斯函数除以d的和等于 欧拉函数除以n的值 6 */ 7 void get_mu(int n) 8 { 9 mu[1]=1; 10 for(int i=2;i<=n;i++) 11 { 12 if(!vis[i]){prim[++cnt]=i;mu[i]=-1;} 13 for(int j=1;j<=cnt&&prim[j]*i<=n;j++) 14 { 15 vis[prim[j]*i]=1; 16 if(i%prim[j]==0)break; 17 else mu[i*prim[j]]=-mu[i]; 18 } 19 } 20
3.12 杜教筛
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long ll; 5 const int maxn = 1700010; 6 int T, tot, prime[maxn], mu[maxn]; 7 map<int, ll> ans_mu; 8 9 void sieve() { 10 fill(prime, prime + maxn, 1); 11 mu[1] = 1, tot = 0; 12 for (int i = 2; i < maxn; i++) { 13 if (prime[i]) { 14 prime[++tot] = i, mu[i] = -1; 15 } 16 for (int j = 1; j <= tot && i * prime[j] < maxn; j++) { 17 prime[i * prime[j]] = 0; 18 if (i % prime[j] == 0) { 19 mu[i * prime[j]] = 0; break; 20 } else { 21 mu[i * prime[j]] = -mu[i]; 22 } 23 } 24 } 25 for (int i = 2; i < maxn; i++) mu[i] += mu[i - 1]; 26 } 27 28 ll calc_mu(int x) { 29 if (x < maxn) return mu[x]; 30 if (ans_mu.count(x)) return ans_mu[x]; 31 ll ans = 1; 32 for (ll i = 2, j; i <= x; i = j + 1) { 33 j = x / (x / i), ans -= (j - i + 1) * calc_mu(x / i); 34 } 35 return ans_mu[x] = ans; 36 } 37 38 ll calc_phi(int x) { 39 ll ans = 0; 40 for (ll i = 1, j; i <= x; i = j + 1) { 41 j = x / (x / i), ans += (x / i) * (x / i) * (calc_mu(j) - calc_mu(i - 1)); 42 } 43 return ((ans - 1) >> 1) + 1; 44 } 45 46 int main() { 47 sieve(); 48 scanf("%d", &T); 49 for (int i = 1, n; i <= T; i++) { 50 scanf("%d", &n); 51 printf("%lld %lld\n", calc_phi(n), calc_mu(n)); 52 } 53 return 0; 54 }
3.13 最大曼哈顿距离
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 #define N 100003 6 #define inf 1e100 7 double a[N][5]; 8 int n; 9 10 int main() { 11 while (scanf("%d", &n) == 1) { 12 for (int i=0; i<n; i++) 13 for (int j=0; j<5; j++) scanf("%lf", &a[i][j]); 14 double ans = 0, mi, mx, t; 15 for (int s=0; s<(1<<5); s++) { 16 mi = inf, mx = -inf; 17 for (int i=0; i<n; i++) { 18 t = 0; 19 for (int j=0; j<5; j++) 20 if ((1<<j) & s) t += a[i][j]; 21 else t -= a[i][j]; 22 mi = min(mi, t); 23 mx = max(mx, t); 24 } 25 ans = max(ans, mx-mi); 26 } 27 printf("%.2lf\n", ans); 28 } 29 return 0; 30 }
四.技巧
4.1 imos累积和法
1 //http://www.hankcs.com/program/algorithm/imos_method.html 2 //算法用于计算二维平面最大高度点,原理左上右下标1,左下右上标-1,累加求最大。 3 #include <iostream> 4 #include <algorithm> 5 #include <bits/stdc++.h> 6 using namespace std; 7 #define W 6 8 #define H 6 9 #define N 4 10 // 左下角坐标 11 int B[N] = {3,4,3,5,}; 12 int C[N] = {0,1,2,2,}; 13 // 右上角坐标 14 int A[N] = {0,3,2,2,}; 15 int D[N] = {3,2,3,5,}; 16 // 地图上的分布结果 17 int tiles[H][W]; 18 19 ///////////////////////////SubMain////////////////////////////////// 20 int main(int argc, char *argv[]) 21 { 22 memset(tiles, 0, sizeof(tiles)); 23 // 影响力计算 (图 3) 24 for (int i = 0; i < N; i++) 25 { 26 tiles[C[i]][A[i]]++; 27 tiles[C[i]][B[i]]--; 28 tiles[D[i]][A[i]]--; 29 tiles[D[i]][B[i]]++; 30 } 31 // 横向累积和 (图 4, 5) 32 for (int y = 0; y < H; y++) 33 { 34 for (int x = 1; x < W; x++) 35 { 36 tiles[y][x] += tiles[y][x - 1]; 37 } 38 } 39 // 纵向累积和 (图 6, 7) 40 for (int y = 1; y < H; y++) 41 { 42 for (int x = 0; x < W; x++) 43 { 44 tiles[y][x] += tiles[y - 1][x]; 45 } 46 } 47 48 cout << *max_element(tiles[0], tiles[0] + H * W) << endl; 49 system("pause"); 50 return 0; 51 } 52 ///////////////////////////End Sub//////////////////////////////////
4.2 坐标离散化
1 //http://www.hankcs.com/program/algorithm/aoj-0531-paint-color.html 2 #include<iostream> 3 #include<vector> 4 #include<algorithm> 5 #include<queue> 6 #include <cstring> 7 #define MAX_N 1000 + 16 8 9 using namespace std; 10 11 int N, H, W; 12 int X1[MAX_N], X2[MAX_N], Y1[MAX_N], Y2[MAX_N]; 13 int fld[2 * MAX_N][2 * MAX_N], // 填充遍历用,代表坐标(i, j)处是否空白(压缩后) 14 dx[4] = { 1, -1, 0, 0 }, dy[4] = { 0, 0, 1, -1 }; 15 16 // 压缩坐标,将坐标的值变成“这是第几种值”,返回一共有几种坐标 17 int compress(int *x1, int *x2, int w) 18 { 19 vector<int>xs; 20 21 for (int i = 0; i < N; ++i) 22 { 23 int tx1 = x1[i], tx2 = x2[i]; 24 if (1 <= tx1 && tx1 < w) xs.push_back(tx1); 25 if (1 <= tx2 && tx2 < w) xs.push_back(tx2); 26 } 27 xs.push_back(0); 28 xs.push_back(w); 29 sort(xs.begin(), xs.end()); 30 xs.erase(unique(xs.begin(), xs.end()), xs.end()); 31 for (int i = 0; i < N; ++i) 32 { 33 x1[i] = find(xs.begin(), xs.end(), x1[i]) - xs.begin(); 34 x2[i] = find(xs.begin(), xs.end(), x2[i]) - xs.begin(); 35 } 36 return xs.size() - 1; 37 } 38 39 int bfs() 40 { 41 int ans = 0; 42 for (int i = 0; i < H; ++i) 43 { 44 for (int j = 0; j < W; ++j) 45 { 46 if (fld[i][j]) continue; 47 ++ans; 48 queue<pair<int, int> >que; 49 que.push(make_pair(j, i)); 50 while (!que.empty()) 51 { 52 int nx = que.front().first, ny = que.front().second; 53 que.pop(); 54 55 for (int i = 0; i < 4; ++i) 56 { 57 int tx = nx + dx[i], ty = ny + dy[i]; 58 if (tx < 0 || W < tx || ty < 0 || H< ty || fld[ty][tx] > 0) continue; 59 que.push(make_pair(tx, ty)); 60 fld[ty][tx] = 1; 61 } 62 } 63 } 64 } 65 return ans; 66 } 67 68 ///////////////////////////SubMain////////////////////////////////// 69 int main(int argc, char *argv[]) 70 { 71 while (cin >> W >> H, W | H) 72 { 73 cin >> N; 74 for (int i = 0; i < N; ++i) 75 { 76 cin >> X1[i] >> Y1[i] >> X2[i] >> Y2[i]; 77 } 78 79 memset(fld, 0, sizeof(fld)); 80 81 W = compress(X1, X2, W); 82 H = compress(Y1, Y2, H); 83 84 // imos-法 85 for (int i = 0; i < N; i++) 86 { 87 fld[Y1[i]][X1[i]]++; 88 fld[Y1[i]][X2[i]]--; 89 fld[Y2[i]][X1[i]]--; 90 fld[Y2[i]][X2[i]]++; 91 } 92 // 横向累积 93 for (int i = 0; i < H; i++) 94 { 95 for (int j = 1; j < W; j++) 96 { 97 fld[i][j] += fld[i][j - 1]; 98 } 99 } 100 // 纵向累积 101 for (int i = 1; i < H; i++) 102 { 103 for (int j = 0; j < W; j++) 104 { 105 fld[i][j] += fld[i - 1][j]; 106 } 107 }// 累积完后,fld中非0部分表示有挡板 108 cout << bfs() << endl; 109 } 110 return 0; 111 } 112 ///////////////////////////End Sub//////////////////////////////////
4.3 博弈sg模板
1 //f[]:可以取走的石子个数 2 //sg[]:0~n的SG函数值 3 //hash[]:mex{} 4 int f[N],sg[N],hash[N]; 5 void getSG(int n) 6 { 7 int i,j; 8 memset(sg,0,sizeof(sg)); 9 for(i=1;i<=n;i++) 10 { 11 memset(hash,0,sizeof(hash)); 12 for(j=1;f[j]<=i;j++) 13 hash[sg[i-f[j]]]=1; 14 for(j=0;j<=n;j++) //求mes{}中未出现的最小的非负整数 15 { 16 if(hash[j]==0) 17 { 18 sg[i]=j; 19 break; 20 } 21 } 22 } 23 }
1 //注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍 2 //n是集合s的大小 S[i]是定义的特殊取法规则的数组 3 int s[110],sg[10010],n; 4 int SG_dfs(int x) 5 { 6 int i; 7 if(sg[x]!=-1) 8 return sg[x]; 9 bool vis[110]; 10 memset(vis,0,sizeof(vis)); 11 for(i=0;i<n;i++) 12 { 13 if(x>=s[i]) 14 { 15 SG_dfs(x-s[i]); 16 vis[sg[x-s[i]]]=1; 17 } 18 } 19 int e; 20 for(i=0;;i++) 21 if(!vis[i]) 22 { 23 e=i; 24 break; 25 } 26 return sg[x]=e; 27 }
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <set> 5 6 using namespace std; 7 8 const int maxn = 10005; 9 10 int sg[maxn]; 11 12 void SG() 13 { 14 sg[0] = 0; 15 for(int i = 2;i <= maxn; i++) { 16 set<int>s; 17 for(int j = 2;j <= 9; j++) { 18 int temp = i/j; 19 if(i%j) temp++; 20 s.insert(sg[temp]); 21 } 22 int now = 0; 23 while(s.count(now)) now++; 24 sg[i] = now; 25 } 26 } 27 /** 28 打表发现:先手1~9,19~162,325~2916必胜 29 满足前一个区间l,r->r*2+1,(r*2)*9 30 */ 31 32 33 int main() 34 { 35 //SG(); 36 //freopen("in.txt","r",stdin); 37 int n; 38 while(scanf("%d",&n) != EOF) { 39 int l = 1,r = 9; 40 int flag = 1; 41 while(1) { 42 if(l <= n && n <= r) { 43 flag = true; 44 break; 45 } 46 else if(n < l) { 47 flag = false; 48 break; 49 } 50 l = r*2+1; 51 r = r*2*9; 52 } 53 if(flag) 54 printf("Stan wins.\n"); 55 else 56 printf("Ollie wins.\n"); 57 } 58 59 return 0; 60 }
4.4 RMQ(区间极值查询)
1 void ST(int n) { 2 for (int i = 1; i <= n; i++) 3 dp[i][0] = A[i]; 4 for (int j = 1; (1 << j) <= n; j++) { 5 for (int i = 1; i + (1 << j) - 1 <= n; i++) { 6 dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]); 7 } 8 } 9 } 10 int RMQ(int l, int r) { 11 int k = 0; 12 while ((1 << (k + 1)) <= r - l + 1) k++; 13 return max(dp[l][k], dp[r - (1 << k) + 1][k]); 14 }
4.5 二维RMQ
1 #include<iostream> 2 #include<cstdio> 3 #include<string.h> 4 #include<string> 5 #include<stack> 6 #include<set> 7 #include<algorithm> 8 #include<cmath> 9 #include<vector> 10 #include<map> 11 12 13 #define ll __int64 14 #define lll unsigned long long 15 #define MAX 1000009 16 #define eps 1e-8 17 18 using namespace std; 19 /* 20 二维RMQ模板题 21 同一维一样 用dp[row][col][i][j]表示(row,col)到(row+2^i,col+2^j)矩形内的最小值 22 查询 23 */ 24 25 int mapp[309][309]; 26 int dp[309][309][9][9]; 27 int flag; 28 29 void RMQ_init2d(int m,int n) 30 { 31 for(int i=1; i<=m; i++) 32 { 33 for(int j = 1; j<=n; j++) 34 { 35 dp[i][j][0][0] = mapp[i][j]; 36 } 37 } 38 int t = log((double)n) / log(2.0); 39 40 for(int i = 0; i<=t; i++) 41 { 42 for(int j = 0; j<=t; j++) 43 { 44 if(i==0&&j==0) 45 continue; 46 for(int row = 1; row+(1<<i)-1<= m; row++) 47 { 48 for(int col = 1; col+(1<<j)-1<= n; col++) 49 { 50 if(i) 51 dp[row][col][i][j] = max(dp[row][col][i-1][j],dp[row+(1<<(i-1))][col][i-1][j]); 52 else 53 dp[row][col][i][j] = max(dp[row][col][i][j-1],dp[row][col+(1<<(j-1))][i][j-1]); 54 } 55 } 56 } 57 } 58 } 59 int RMQ_2d(int x1,int y1,int x2,int y2) 60 { 61 int k1 = log(double(x2 - x1 + 1)) / log(2.0); 62 int k2 = log(double(y2 - y1 + 1)) / log(2.0); 63 int m1 = dp[x1][y1][k1][k2]; 64 int m2 = dp[x2 - (1<<k1) + 1][y1][k1][k2]; 65 int m3 = dp[x1][y2 - (1<<k2) + 1][k1][k2]; 66 int m4 = dp[x2 - (1<<k1) + 1][y2 - (1<<k2) + 1 ][k1][k2]; 67 int _max = max(max(m1,m2),max(m3,m4)); 68 if(mapp[x1][y1]==_max||mapp[x1][y2]==_max||mapp[x2][y1]==_max||mapp[x2][y2]==_max) 69 flag = 1; 70 return _max; 71 } 72 73 int main() 74 { 75 int n,m,t; 76 int x1,x2,y1,y2; 77 while(~scanf("%d%d",&m,&n)) 78 { 79 for(int i = 1; i<=m; i++) 80 { 81 for(int j = 1; j<=n; j++) 82 { 83 scanf("%d",&mapp[i][j]); 84 } 85 } 86 RMQ_init2d(m,n); 87 scanf("%d",&t); 88 while(t--) 89 { 90 scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 91 92 flag = 0; 93 int _max = RMQ_2d(x1,y1,x2,y2); 94 if(flag == 1) 95 printf("%d yes\n",_max); 96 else 97 printf("%d no\n",_max); 98 } 99 } 100 return 0; 101 }
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; int val[255][255]; int mm[255]; int dpmin[255][255][8][8];//最小值 int dpmax[255][255][8][8];//最大值 void initRMQ(int n,int m) { for(int i = 1;i <= n;i++) for(int j = 1;j <= m;j++) dpmin[i][j][0][0] = dpmax[i][j][0][0] = val[i][j]; for(int ii = 0; ii <= mm[n]; ii++) for(int jj = 0; jj <= mm[m]; jj++) if(ii+jj) for(int i = 1; i + (1<<ii) - 1 <= n; i++) for(int j = 1; j + (1<<jj) - 1 <= m; j++) { if(ii) { dpmin[i][j][ii][jj] = min(dpmin[i][j][ii-1][jj],dpmin[i+(1<<(ii-1))][j][ii-1][jj]); dpmax[i][j][ii][jj] = max(dpmax[i][j][ii-1][jj],dpmax[i+(1<<(ii-1))][j][ii-1][jj]); } else { dpmin[i][j][ii][jj] = min(dpmin[i][j][ii][jj-1],dpmin[i][j+(1<<(jj-1))][ii][jj-1]); dpmax[i][j][ii][jj] = max(dpmax[i][j][ii][jj-1],dpmax[i][j+(1<<(jj-1))][ii][jj-1]); } } } //查询矩形的最大值 int rmq1(int x1,int y1,int x2,int y2) { int k1 = mm[x2-x1+1]; int k2 = mm[y2-y1+1]; x2 = x2 - (1<<k1) + 1; y2 = y2 - (1<<k2) + 1; return max(max(dpmax[x1][y1][k1][k2],dpmax[x1][y2][k1][k2]),max(dpmax[x2][y1][k1][k2],dpmax[x2][y2][k1][k2])); } //查询矩形的最小值 int rmq2(int x1,int y1,int x2,int y2) { int k1 = mm[x2-x1+1]; int k2 = mm[y2-y1+1]; x2 = x2 - (1<<k1) + 1; y2 = y2 - (1<<k2) + 1; return min(min(dpmin[x1][y1][k1][k2],dpmin[x1][y2][k1][k2]),min(dpmin[x2][y1][k1][k2],dpmin[x2][y2][k1][k2])); } int main() { mm[0] = -1; for(int i = 1;i <= 500;i++) mm[i] = ((i&(i-1))==0)?mm[i-1]+1:mm[i-1]; int N,B,K; while(scanf("%d%d%d",&N,&B,&K)==3) { for(int i = 1;i <= N;i++) for(int j = 1;j <= N;j++) scanf("%d",&val[i][j]); initRMQ(N,N); int x,y; while(K--) { scanf("%d%d",&x,&y); printf("%d\n",rmq1(x,y,x+B-1,y+B-1)-rmq2(x,y,x+B-1,y+B-1)); } } return 0; }
4.6 莫队算法
1 #include<stdio.h> 2 #include<algorithm> 3 #include<iostream> 4 #include<math.h> 5 #include<cstring> 6 #define go(i,a,b) for(int i=a;i<=b;i++) 7 #define mem(a,b) memset(a,b,sizeof(a)) 8 #define ll long long 9 using namespace std;const int N=50003; 10 struct Mo{int l,r,ID;ll A,B;}q[N];ll S(ll x){return x*x;} 11 ll GCD(ll a,ll b){while(b^=a^=b^=a%=b);return a;} 12 int n,m,col[N],unit,Be[N];ll sum[N],ans; 13 bool cmp(Mo a,Mo b){return Be[a.l]==Be[b.l]?a.r<b.r:a.l<b.l;} 14 bool CMP(Mo a,Mo b){return a.ID<b.ID;}; 15 void revise(int x,int add){ans-=S(sum[col[x]]),sum[col[x]]+=add,ans+=S(sum[col[x]]);} 16 int main() 17 { 18 scanf("%d%d",&n,&m);unit=sqrt(n); 19 go(i,1,n)scanf("%d",&col[i]),Be[i]=i/unit+1;; 20 go(i,1,m)scanf("%d%d",&q[i].l,&q[i].r),q[i].ID=i; 21 22 sort(q+1,q+m+1,cmp); 23 24 int l=1,r=0; 25 go(i,1,m) 26 { 27 while(l<q[i].l)revise(l,-1),l++; 28 while(l>q[i].l)revise(l-1,1),l--; 29 while(r<q[i].r)revise(r+1,1),r++; 30 while(r>q[i].r)revise(r,-1),r--; 31 32 if(q[i].l==q[i].r){q[i].A=0;q[i].B=1;continue;} 33 q[i].A=ans-(q[i].r-q[i].l+1); 34 q[i].B=1LL*(q[i].r-q[i].l+1)*(q[i].r-q[i].l); 35 ll gcd=GCD(q[i].A,q[i].B);q[i].A/=gcd;q[i].B/=gcd; 36 } 37 38 sort(q+1,q+m+1,CMP); 39 go(i,1,m)printf("%lld/%lld\n",q[i].A,q[i].B); 40 return 0; 41 }//Paul_Guderian
带修莫队
1 #include<stdio.h> 2 #include<algorithm> 3 #include<math.h> 4 #define go(i,a,b) for(int i=a;i<=b;i++) 5 using namespace std;const int N=10003; 6 struct Query{int l,r,Tim,ID;}q[N]; 7 struct Change{int pos,New,Old;}c[N]; 8 int n,m,s[N],color[N*100],t,Time,now[N],unit,Be[N],ans[N],Ans,l=1,r,T; 9 bool cmp(Query a,Query b) 10 { 11 return Be[a.l]==Be[b.l]?(Be[a.r]==Be[b.r]?a.Tim<b.Tim:a.r<b.r):a.l<b.l; 12 } 13 void revise(int x,int d){color[x]+=d;if(d>0)Ans+=color[x]==1;if(d<0)Ans-=color[x]==0;} 14 void going(int x,int d){if(l<=x&&x<=r)revise(d,1),revise(s[x],-1);s[x]=d;} 15 int main(){ 16 scanf("%d%d",&n,&m);unit=pow(n,0.666666); 17 go(i,1,n)scanf("%d",&s[i]),now[i]=s[i],Be[i]=i/unit+1; 18 go(i,1,m){char sign;int x,y;scanf(" %c %d%d",&sign,&x,&y); 19 if(sign=='Q')q[++t]=(Query){x,y,Time,t}; 20 if(sign=='R')c[++Time]=(Change){x,y,now[x]},now[x]=y; 21 } 22 sort(q+1,q+t+1,cmp);go(i,1,t) 23 { 24 while(T<q[i].Tim)going(c[T+1].pos,c[T+1].New),T++; 25 while(T>q[i].Tim)going(c[T].pos,c[T].Old),T--; 26 27 while(l<q[i].l)revise(s[l],-1),l++; 28 while(l>q[i].l)revise(s[l-1],1),l--; 29 while(r<q[i].r)revise(s[r+1],1),r++; 30 while(r>q[i].r)revise(s[r],-1),r--; 31 32 ans[q[i].ID]=Ans; 33 } 34 go(i,1,t)printf("%d\n",ans[i]);return 0; 35 }//Paul_Guderian
树上莫队
1 #include<stdio.h> 2 #include<algorithm> 3 #include<math.h> 4 #define go(i,a,b) for(int i=a;i<=b;i++) 5 #define ro(i,a,b) for(int i=a;i>=b;i--) 6 #define fo(i,a,x) for(int i=a[x],v=e[i].v;i;i=e[i].next,v=e[i].v) 7 using namespace std;const int N=50009; 8 struct E{int v,next;}e[N*3]; 9 int k=1,head[N],unit,Be[N],m,st[N],top,fa[N][18],deep[N]; 10 int n,Q,a[N],t[N],op,x,y,p,tim,u=1,v=1,T,ans[N],vis[N]; 11 void ADD(int u,int v){e[k]=(E){v,head[u]};head[u]=k++;} 12 void dfs(int u){ 13 14 go(i,1,19)if((1<<i)>deep[u])break; 15 else fa[u][i]=fa[fa[u][i-1]][i-1]; 16 17 int bottom=top; 18 fo(i,head,u)if(v!=fa[u][0]) 19 { 20 fa[v][0]=u;deep[v]=deep[u]+1;dfs(v); 21 if(top-bottom>=unit){m++;while(top!=bottom)Be[st[top--]]=m;} 22 } 23 st[++top]=u; 24 } 25 int LCA(int x,int y) 26 { 27 if(deep[x]<deep[y])swap(x,y);int Dis=deep[x]-deep[y]; 28 go(i,0,16)if((1<<i)&Dis)x=fa[x][i]; 29 if(x==y)return x; 30 ro(i,16,0)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i]; 31 return x==y?x:fa[x][0]; 32 } 33 struct Change{int u,New,Old;}cq[N]; 34 struct Query{int u,v,tim,id;bool operator <(const Query &a) const{ 35 return Be[u]==Be[a.u]?(Be[v]==Be[a.v]?tim<a.tim:Be[v]<Be[a.v]):Be[u]<Be[a.u]; 36 }}q[N]; 37 struct Datalock{ 38 struct _blo{int l,r;}b[350]; 39 int n,Be[N],m,unit,num[N],sum[350]; 40 void init() 41 { 42 unit=sqrt(n);m=(n-1)/unit+1; 43 go(i,1,n)Be[i]=(i-1)/unit+1; 44 go(i,1,m)b[i].l=(i-1)*unit+1,b[i].r=i*unit; 45 b[m].r=n; 46 } 47 void Add(int v){if(v<=n)sum[Be[v]]+=(++num[v])==1;} 48 void Del(int v){if(v<=n)sum[Be[v]]-=(--num[v])==0;} 49 int mex() 50 { 51 go(i,1,m)if(sum[i]!=b[i].r-b[i].l+1) 52 go(j,b[i].l,b[i].r)if(!num[j])return j; 53 return -1; 54 } 55 }Data; 56 void revise(int u,int d){if(vis[u])Data.Del(a[u]),Data.Add(d);a[u]=d;} 57 void Run(int u){if(vis[u])Data.Del(a[u]),vis[u]=0;else Data.Add(a[u]),vis[u]=1;} 58 void move(int x,int y) 59 { 60 if(deep[x]<deep[y])swap(x,y); 61 while(deep[x]>deep[y])Run(x),x=fa[x][0]; 62 while(x!=y)Run(x),Run(y),x=fa[x][0],y=fa[y][0]; 63 } 64 void Mo() 65 { 66 go(i,1,p) 67 { 68 while(T<q[i].tim)T++,revise(cq[T].u,cq[T].New); 69 while(T>q[i].tim)revise(cq[T].u,cq[T].Old),T--; 70 71 if(u!=q[i].u)move(u,q[i].u),u=q[i].u; 72 if(v!=q[i].v)move(v,q[i].v),v=q[i].v; 73 int anc=LCA(u,v);Run(anc);ans[q[i].id]=Data.mex()-1;Run(anc); 74 } 75 } 76 int main(){scanf("%d%d",&n,&Q);unit=pow(n,0.45); 77 go(i,1,n)scanf("%d",&a[i]),t[i]=++a[i]; 78 go(i,2,n){int uu,vv;scanf("%d%d",&uu,&vv);ADD(uu,vv);ADD(vv,uu);} 79 dfs(1);while(top)Be[st[top--]]=m; 80 go(i,1,Q) 81 { 82 scanf("%d%d%d",&op,&x,&y); 83 if( op)p++,q[p]=(Query){x,y,tim,p}; 84 if(!op)tim++,cq[tim]=(Change){x,y+1,t[x]},t[x]=y+1; 85 } 86 Data.n=n+1;Data.init();sort(q+1,q+1+p);Mo(); 87 go(i,1,p)printf("%d\n",ans[i]); 88 }//Paul_Guderian
C(n,k)
1 /*20180802 2 题意:求sum(C(n,k)) 0<=i<=k; 3 数据范围1e5. 4 莫队算法:推出递推公式。 5 按块排序使得顺序解题效率最优 6 */ 7 #include<stdio.h> 8 #include<algorithm> 9 #include<iostream> 10 #include<math.h> 11 #include<cstring> 12 #define go(i,a,b) for(int i=a;i<=b;i++) 13 #define mem(a,b) memset(a,b,sizeof(a)) 14 #define ll long long 15 using namespace std; 16 const int N=100100; 17 const int MOD=1000000007; 18 int fac[N], inv[N], res[N], in_chunk[N]; 19 int cnt, mx=100010, chunk,T; 20 //每次询问 21 struct Mo{int n,k,ID;ll ans;}q[N]; 22 23 int powi(int a, int b) 24 { 25 int c = 1; 26 for (; b; b >>= 1, a = 1ll * a * a % MOD) 27 if (b & 1) c = 1ll * c * a % MOD; 28 return c; 29 } 30 int C(int a, int b) 31 { 32 return 1ll * fac[a] * inv[b] % MOD * inv[a - b] % MOD; 33 } 34 35 int n,unit,Be[N];ll ans; 36 //按块双关键词排序,使得k在变化过程中一定比n小 37 bool cmp(Mo a,Mo b){return Be[a.k]==Be[b.k]?a.n<b.n:a.k<b.k;} 38 bool CMP(Mo a,Mo b){return a.ID<b.ID;} 39 40 int main() 41 { 42 43 fac[0] = 1; for (int i = 1; i <= 100010; ++ i) fac[i] = 1ll * fac[i - 1] * i % MOD; 44 inv[mx] = powi(fac[mx], MOD - 2); for (int i = mx - 1; ~i; -- i) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD; 45 46 47 scanf("%d",&T);unit=sqrt(100000); 48 for(int i=0;i<=100000;i++)Be[i]=i/unit+1; 49 go(i,1,T)scanf("%d%d",&q[i].n,&q[i].k),q[i].ID=i; 50 51 sort(q+1,q+T+1,cmp); 52 int in=0,ik=0; 53 ans=1; 54 go(i,1,T) 55 { 56 while(in<q[i].n){ 57 if(ik==0){ 58 in=q[i].n; 59 ans=1; 60 } 61 else { 62 ans=(2*ans-C(in,ik)+MOD)%MOD; 63 in++; 64 } 65 } 66 while(in>q[i].n){ 67 if(ik==0){ 68 in=q[i].n; 69 ans=1; 70 } 71 else { 72 ans=(ans+C(in-1,ik))%MOD*inv[2]%MOD; 73 in--; 74 } 75 } 76 while(ik<q[i].k){ 77 ans=(ans+C(in,ik+1))%MOD; 78 ik++; 79 } 80 while(ik>q[i].k){ 81 ans=(ans-C(in,ik)+MOD)%MOD; 82 ik--; 83 } 84 q[i].ans=ans; 85 } 86 sort(q+1,q+T+1,CMP); 87 go(i,1,T)printf("%lld\n",q[i].ans); 88 return 0; 89 }//Paul_Guderian
区间覆盖问题
1 #include<iostream> 2 #include<stdio.h> 3 #include<algorithm> 4 using namespace std; 5 6 7 int main() 8 { 9 int T, n, s[250001], t[250001]; 10 int m; 11 double sum=0; 12 cin >> n>>m; 13 for (int i = 1; i <= n; i++){ 14 scanf("%d%d", &s[i], &t[i]); 15 sum+=t[i]-s[i]+1; 16 } 17 sort(s + 1, s + n + 1); 18 sort(t + 1, t + n + 1); 19 int ans = 0; 20 for (int i = 1, j = 1; i <= n;) 21 { 22 if (s[i] <= t[j]) 23 { 24 if (ans < i - j)ans = i - j; 25 i++; 26 } 27 else j++; 28 } 29 cout << ans + 1 << endl; 30 printf("%.20lf\n",sum/m); 31 32 return 0; 33 }
五.图论
5.1 并查集
1 //https://blog.csdn.net/u013486414/article/details/38682057 2 const int MAXSIZE = 500; 3 4 int uset[MAXSIZE]; 5 6 int rank[MAXSIZE]; 7 8 9 10 void makeSet(int size) { 11 12 for(int i = 0;i < size;i++) uset[i] = i; 13 14 for(int i = 0;i < size;i++) rank[i] = 0; 15 16 } 17 18 int find(int x) { 19 20 if (x != uset[x]) uset[x] = find(uset[x]); 21 22 return uset[x]; 23 24 } 25 26 void unionSet(int x, int y) { 27 28 if ((x = find(x)) == (y = find(y))) return; 29 30 if (rank[x] > rank[y]) uset[y] = x; 31 32 else { 33 34 uset[x] = y; 35 36 if (rank[x] == rank[y]) rank[y]++; 37 38 } 39 40 }
5.2 求图中任意两点的最短距离的 弗洛伊德算法
1 //https://blog.csdn.net/qq_16657927/article/details/79942140 2 /* 3 |Floyd算法| 4 |任意点对最短路算法| 5 |求图中任意两点的最短距离的算法| 6 */ 7 8 for (int k = 0; k < n; k++) { 9 for (int i = 0; i < n; i++) { 10 for (int j = 0; j < n; j++) { 11 dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]); 12 } 13 } 14 }
5.3 最小生成树·Kruskal
1 //https://blog.csdn.net/qq_16657927/article/details/79942140 2 /* 3 |Kruskal算法| 4 |适用于 稀疏图 求最小生成树| 5 |16/11/05ztx thanks to wangqiqi| 6 */ 7 8 /* 9 第一步:点、边、加入vector,把所有边按从小到大排序 10 第二步:并查集部分 + 下面的code 11 */ 12 13 void Kruskal() { 14 ans = 0; 15 for (int i = 0; i<len; i++) { 16 if (Find(edge[i].a) != Find(edge[i].b)) { 17 Union(edge[i].a, edge[i].b); 18 ans += edge[i].len; 19 } 20 } 21 }
5.4 最小生成树· prim算法
1 //https://blog.csdn.net/qq_16657927/article/details/79942140 2 /* 3 |Prim算法| 4 |适用于 稠密图 求最小生成树| 5 |堆优化版,时间复杂度:O(elgn)| 6 |16/11/05ztx, thanks to chaixiaojun| 7 */ 8 9 struct node { 10 int v, len; 11 node(int v = 0, int len = 0) :v(v), len(len) {} 12 bool operator < (const node &a)const { // 加入队列的元素自动按距离从小到大排序 13 return len> a.len; 14 } 15 }; 16 17 vector<node> G[maxn]; 18 int vis[maxn]; 19 int dis[maxn]; 20 21 void init() { 22 for (int i = 0; i<maxn; i++) { 23 G[i].clear(); 24 dis[i] = INF; 25 vis[i] = false; 26 } 27 } 28 int Prim(int s) { 29 priority_queue<node>Q; // 定义优先队列 30 int ans = 0; 31 Q.push(node(s,0)); // 起点加入队列 32 while (!Q.empty()) { 33 node now = Q.top(); Q.pop(); // 取出距离最小的点 34 int v = now.v; 35 if (vis[v]) continue; // 同一个节点,可能会推入2次或2次以上队列,这样第一个被标记后,剩下的需要直接跳过。 36 vis[v] = true; // 标记一下 37 ans += now.len; 38 for (int i = 0; i<G[v].size(); i++) { // 开始更新 39 int v2 = G[v][i].v; 40 int len = G[v][i].len; 41 if (!vis[v2] && dis[v2] > len) { 42 dis[v2] = len; 43 Q.push(node(v2, dis[v2])); // 更新的点加入队列并排序 44 } 45 } 46 } 47 return ans; 48 }
1 //https://blog.csdn.net/qq_16657927/article/details/79942140 2 #include<iostream> 3 #include<string> 4 #include<vector> 5 using namespace std; 6 7 //首先是使用邻接矩阵完成Prim算法 8 struct Graph { 9 int vexnum; //顶点个数 10 int edge; //边的条数 11 int ** arc; //邻接矩阵 12 string *information; //记录每个顶点名称 13 }; 14 15 //创建图 16 void createGraph(Graph & g) { 17 cout << "请输入顶点数:输入边的条数" << endl; 18 cin >> g.vexnum; 19 cin >> g.edge; //输入边的条数 20 21 g.information = new string[g.vexnum]; 22 g.arc = new int*[g.vexnum]; 23 int i = 0; 24 25 //开辟空间的同时,进行名称的初始化 26 for (i = 0; i < g.vexnum; i++) { 27 g.arc[i] = new int[g.vexnum]; 28 g.information[i]="v"+ std::to_string(i+1);//对每个顶点进行命名 29 for (int k = 0; k < g.vexnum; k++) { 30 g.arc[i][k] = INT_MAX; //初始化我们的邻接矩阵 31 } 32 } 33 34 cout << "请输入每条边之间的顶点编号(顶点编号从1开始),以及该边的权重:" << endl; 35 for (i = 0; i < g.edge; i++) { 36 int start; 37 int end; 38 cin >> start; //输入每条边的起点 39 cin >> end; //输入每条边的终点 40 int weight; 41 cin >> weight; 42 g.arc[start-1][end-1]=weight;//无向图的边是相反的 43 g.arc[end-1][start-1] = weight; 44 } 45 } 46 47 //打印图 48 void print(Graph g) { 49 int i; 50 for (i = 0; i < g.vexnum; i++) { 51 //cout << g.information[i] << " "; 52 for (int j = 0; j < g.vexnum; j++) { 53 if (g.arc[i][j] == INT_MAX) 54 cout << "∞" << " "; 55 else 56 cout << g.arc[i][j] << " "; 57 } 58 cout << endl; 59 } 60 } 61 62 //作为记录边的信息,这些边都是达到end的所有边中,权重最小的那个 63 struct Assis_array { 64 int start; //边的终点 65 int end; //边的起点 66 int weight; //边的权重 67 }; 68 //进行prim算法实现,使用的邻接矩阵的方法实现。 69 void Prim(Graph g,int begin) { 70 71 //close_edge这个数组记录到达某个顶点的各个边中的权重最大的那个边 72 Assis_array *close_edge=new Assis_array[g.vexnum]; 73 74 int j; 75 76 //进行close_edge的初始化,更加开始起点进行初始化 77 for (j = 0; j < g.vexnum; j++) { 78 if (j != begin - 1) { 79 close_edge[j].start = begin-1; 80 close_edge[j].end = j; 81 close_edge[j].weight = g.arc[begin - 1][j]; 82 } 83 } 84 //把起点的close_edge中的值设置为-1,代表已经加入到集合U了 85 close_edge[begin - 1].weight = -1; 86 //访问剩下的顶点,并加入依次加入到集合U 87 for (j = 1; j < g.vexnum; j++) { 88 89 int min = INT_MAX; 90 int k; 91 int index; 92 //寻找数组close_edge中权重最小的那个边 93 for (k = 0; k < g.vexnum; k++) { 94 if (close_edge[k].weight != -1) { 95 if (close_edge[k].weight < min) { 96 min = close_edge[k].weight; 97 index = k; 98 } 99 } 100 } 101 //将权重最小的那条边的终点也加入到集合U 102 close_edge[index].weight = -1; 103 //输出对应的边的信息 104 cout << g.information[close_edge[index].start] 105 << "-----" 106 << g.information[close_edge[index].end] 107 << "=" 108 <<g.arc[close_edge[index].start][close_edge[index].end] 109 <<endl; 110 111 //更新我们的close_edge数组。 112 for (k = 0; k < g.vexnum; k++) { 113 if (g.arc[close_edge[index].end][k] <close_edge[k].weight) { 114 close_edge[k].weight = g.arc[close_edge[index].end][k]; 115 close_edge[k].start = close_edge[index].end; 116 close_edge[k].end = k; 117 } 118 } 119 } 120 } 121 122 123 124 int main() 125 { 126 Graph g; 127 createGraph(g);//基本都是无向网图,所以我们只实现了无向网图 128 print(g); 129 Prim(g, 1); 130 system("pause"); 131 return 0; 132 }
5.5 染色法
1 /* 2 |交叉染色法判断二分图| 3 |16/11/05ztx| 4 */ 5 6 int bipartite(int s) { 7 int u, v; 8 queue<int>Q; 9 color[s] = 1; 10 Q.push(s); 11 while (!Q.empty()) { 12 u = Q.front(); 13 Q.pop(); 14 for (int i = 0; i < G[u].size(); i++) { 15 v = G[u][i]; 16 if (color[v] == 0) { 17 color[v] = -color[u]; 18 Q.push(v); 19 } 20 else if (color[v] == color[u]) 21 return 0; 22 } 23 } 24 return 1; 25 }
5.6 匈牙利算法
1 //https://blog.csdn.net/qq_16657927/article/details/79942140 2 /* 3 |求解最大匹配问题| 4 |递归实现| 5 |16/11/05ztx| 6 */ 7 8 vector<int>G[maxn]; 9 bool inpath[maxn]; // 标记 10 int match[maxn]; // 记录匹配对象 11 void init() 12 { 13 memset(match, -1, sizeof(match)); 14 for (int i = 0; i < maxn; ++i) { 15 G[i].clear(); 16 } 17 } 18 bool findpath(int k) { 19 for (int i = 0; i < G[k].size(); ++i) { 20 int v = G[k][i]; 21 if (!inpath[v]) { 22 inpath[v] = true; 23 if (match[v] == -1 || findpath(match[v])) { // 递归 24 match[v] = k; // 即匹配对象是“k妹子”的 25 return true; 26 } 27 } 28 } 29 return false; 30 } 31 32 void hungary() { 33 int cnt = 0; 34 for (int i = 1; i <= m; i++) { // m为需要匹配的“妹子”数 35 memset(inpath, false, sizeof(inpath)); // 每次都要初始化 36 if (findpath(i)) cnt++; 37 } 38 cout << cnt << endl; 39 }
1 /* 2 |求解最大匹配问题| 3 |dfs实现| 4 |16/11/05ztx| 5 */ 6 7 int v1, v2; 8 bool Map[501][501]; 9 bool visit[501]; 10 int link[501]; 11 int result; 12 13 bool dfs(int x) { 14 for (int y = 1; y <= v2; ++y) { 15 if (Map[x][y] && !visit[y]) { 16 visit[y] = true; 17 if (link[y] == 0 || dfs(link[y])) { 18 link[y] = x; 19 return true; 20 } } } 21 return false; 22 } 23 24 25 void Search() { 26 for (int x = 1; x <= v1; x++) { 27 memset(visit,false,sizeof(visit)); 28 if (dfs(x)) 29 result++; 30 } 31 }
5.7 Dijstra算法
1 //https://blog.csdn.net/mengxiang000000/article/details/50421243 2 #include<stdio.h> 3 #include<string.h> 4 #include<iostream> 5 using namespace std; 6 #define N 0x1f1f1f1f 7 int w[151][151]; 8 int d[155]; 9 int ans,vis[151]; 10 int n,m; 11 void Dij() 12 { 13 int i,j,k,v,tmp; 14 memset(vis,0,sizeof(vis)); 15 for(i=1;i<=n;i++) 16 d[i]=w[1][i]; 17 d[1]=0; 18 vis[1]=1; 19 for(i=1;i<=n;i++) 20 { 21 tmp=N; 22 for(j=1;j<=n;j++) 23 { 24 if(tmp>d[j]&&!vis[j]) 25 { 26 tmp=d[j]; 27 v=j; 28 } 29 } 30 vis[v]=1; 31 for(k=1;k<=n;k++) 32 { 33 if(!vis[k]) 34 d[k]=min(d[k],d[v]+w[v][k]); 35 } 36 } 37 } 38 int main() 39 { 40 while(~scanf("%d%d",&n,&m)) 41 { 42 if(n==0&&m==0)break; 43 for(int i=1;i<=n;i++) 44 { 45 for(int j=1;j<=n;j++) 46 { 47 w[i][j]=0x1f1f1f1f; 48 } 49 } 50 for(int i=0;i<m;i++) 51 { 52 int a,b,dis; 53 scanf("%d%d%d",&a,&b,&dis); 54 if(w[a][b]>dis) 55 w[a][b]=w[b][a]=dis; 56 } 57 Dij(); 58 printf("%d\n",d[n]); 59 } 60 }
5.8 Bellman-ford算法
1 struct edge{int from,to,cost;}; 2 3 edge es[MAX_E]; 4 5 int d[MAX_V]; 6 int V,E; 7 8 void shortest(int k){ 9 memset(d,0x3f,sizeof(d)); 10 d[k]=0; 11 while(true){ 12 bool update=false; 13 for(int i=0;i<E;i++){ 14 edge e=es[i]; 15 if(d[e.from]!=INF&&d[e.from]+e.cost<d[e.to]){ 16 d[e.to]=d[e.from]+e.cost; 17 update=true; 18 } 19 } 20 if(!update)break; 21 } 22 }
5.11 拓扑排序
1 #include <iostream> 2 #include <iostream> 3 #include <cstdio> 4 #include <algorithm> 5 #include <cstring> 6 #include <sstream> 7 #include <set> 8 #include <map> 9 #include <queue> 10 #include <stack> 11 #include <cmath> 12 #define nmax 200 13 #define MEM(x) memset(x,0,sizeof(x)) 14 using namespace std; 15 vector<int> v[nmax]; 16 int indegree[nmax]; 17 int n; 18 bool suc = true; 19 queue<int> ans; 20 void topsort() 21 { 22 queue<int> q; 23 while(1){ 24 for(int i = 1; i<=n ;++i){ 25 if(indegree[i] == 0){ 26 q.push(i); 27 ans.push(i); 28 indegree[i] = -1; 29 } 30 } 31 if(q.empty()) break; 32 while(!q.empty()){ 33 int t = q.front(); q.pop(); 34 for(int j = 0;j<v[t].size();++j){ 35 int tt = v[t][j]; 36 if(indegree[tt] == -1){ 37 suc = false; 38 break; 39 }else indegree[tt]--; 40 } 41 v[t].clear(); 42 if(!suc) break; 43 } 44 if(!suc) break; 45 } 46 if(ans.size() <n){ 47 suc =false; 48 return; 49 } 50 } 51 void output() 52 { 53 bool isfirst = true; 54 while(!ans.empty()){ 55 int t = ans.front(); ans.pop(); 56 if(isfirst){ 57 printf("%d",t); 58 isfirst = false; 59 }else 60 printf(" %d",t); 61 } 62 printf("\n"); 63 } 64 int main() 65 { 66 //freopen("in.txt","r",stdin); 67 int m; 68 while(scanf("%d%d",&n,&m) ==2 && (n||m)){ 69 MEM(indegree); 70 suc = true; 71 int a,b; 72 for(int i = 0; i<m; ++i){ 73 scanf("%d%d",&a,&b); 74 indegree[b]++; 75 v[a].push_back(b); 76 } 77 topsort(); 78 if(suc) output(); 79 else printf("failed\n"); 80 } 81 return 0; 82 }
5.12 数字游戏dfs记录路径
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 using namespace std; 6 7 typedef long long LL; 8 9 bool flag; 10 LL a, b; 11 int tp; 12 LL link[1014]; 13 14 void dfs(LL x); 15 16 int main(){ 17 while(~scanf("%lld %lld", &a, &b)){ 18 tp = 0, flag = false; 19 link[tp++] = a; 20 dfs(a); 21 if(flag) { 22 printf("YES\n"); 23 for(int i = 0; i < tp; i++) 24 printf("%lld%c", link[i], i == tp-1? '\n': ' '); 25 } 26 else { 27 printf("NO\n"); 28 } 29 } 30 return 0; 31 } 32 void dfs(LL x){ 33 if(flag || x > b) return; 34 35 link[tp++] = x<<1; 36 if(link[tp-1] == b){ 37 flag = true; 38 return; 39 } 40 dfs(link[tp-1]); 41 if(flag) return; 42 else tp--; 43 44 link[tp++] = x*(LL)10 + (LL)1; 45 if(link[tp-1] == b){ 46 flag = true; 47 return; 48 } 49 dfs(link[tp-1]); 50 if(flag) return; 51 else tp--; 52 53 return; 54 }
5.13 最长路径
// A C++ program to find single source longest distances in a DAG #include <iostream> #include <list> #include <stack> #include <limits.h> #define NINF INT_MIN using namespace std; //图通过邻接表来描述。邻接表中的每个顶点包含所连接的顶点的数据,以及边的权值。 class AdjListNode { int v; int weight; public: AdjListNode(int _v, int _w) { v = _v; weight = _w;} int getV() { return v; } int getWeight() { return weight; } }; // Class to represent a graph using adjacency list representation class Graph { int V; // No. of vertices’ // Pointer to an array containing adjacency lists list<AdjListNode> *adj; // A function used by longestPath void topologicalSortUtil(int v, bool visited[], stack<int> &Stack); public: Graph(int V); // Constructor // function to add an edge to graph void addEdge(int u, int v, int weight); // Finds longest distances from given source vertex void longestPath(int s); }; Graph::Graph(int V) // Constructor { this->V = V; adj = new list<AdjListNode>[V]; } void Graph::addEdge(int u, int v, int weight) { AdjListNode node(v, weight); adj[u].push_back(node); // Add v to u’s list } // 通过递归求出拓扑序列. 详细描述,可参考下面的链接。 // http://www.geeksforgeeks.org/topological-sorting/ void Graph::topologicalSortUtil(int v, bool visited[], stack<int> &Stack) { // 标记当前顶点为已访问 visited[v] = true; // 对所有邻接点执行递归调用 list<AdjListNode>::iterator i; for (i = adj[v].begin(); i != adj[v].end(); ++i) { AdjListNode node = *i; if (!visited[node.getV()]) topologicalSortUtil(node.getV(), visited, Stack); } // 当某个点没有邻接点时,递归结束,将该点存入栈中。 Stack.push(v); } // 根据传入的顶点,求出到到其它点的最长路径. longestPath使用了 // topologicalSortUtil() 方法获得顶点的拓扑序。 void Graph::longestPath(int s) { stack<int> Stack; int dist[V]; // 标记所有的顶点为未访问 bool *visited = new bool[V]; for (int i = 0; i < V; i++) visited[i] = false; // 对每个顶点调用topologicalSortUtil,最终求出图的拓扑序列存入到Stack中。 for (int i = 0; i < V; i++) if (visited[i] == false) topologicalSortUtil(i, visited, Stack); //初始化到所有顶点的距离为负无穷 //到源点的距离为0 for (int i = 0; i < V; i++) dist[i] = NINF; dist[s] = 0; // 处理拓扑序列中的点 while (Stack.empty() == false) { //取出拓扑序列中的第一个点 int u = Stack.top(); Stack.pop(); // 更新到所有邻接点的距离 list<AdjListNode>::iterator i; if (dist[u] != NINF) { for (i = adj[u].begin(); i != adj[u].end(); ++i) if (dist[i->getV()] < dist[u] + i->getWeight()) dist[i->getV()] = dist[u] + i->getWeight(); } } // 打印最长路径 for (int i = 0; i < V; i++) (dist[i] == NINF)? cout << "INF ": cout << dist[i] << " "; } // Driver program to test above functions int main() { // Create a graph given in the above diagram. Here vertex numbers are // 0, 1, 2, 3, 4, 5 with following mappings: // 0=r, 1=s, 2=t, 3=x, 4=y, 5=z Graph g(6); g.addEdge(0, 1, 5); g.addEdge(0, 2, 3); g.addEdge(1, 3, 6); g.addEdge(1, 2, 2); g.addEdge(2, 4, 4); g.addEdge(2, 5, 2); g.addEdge(2, 3, 7); g.addEdge(3, 5, 1); g.addEdge(3, 4, -1); g.addEdge(4, 5, -2); int s = 1; cout << "Following are longest distances from source vertex " << s <<" \n"; g.longestPath(s); return 0; }
六.数据结构
6.1 滑动区间最大值
1 #include<cstdio> 2 const int N=10000010; 3 int T,n,m,k,P,Q,R,MOD,i,a[N],q[N],h,t;long long A,B; 4 int main(){ 5 scanf("%d",&T); 6 while(T--){ 7 scanf("%d%d%d%d%d%d%d",&n,&m,&k,&P,&Q,&R,&MOD); 8 for(i=1;i<=k;i++)scanf("%d",&a[i]); 9 for(i=k+1;i<=n;i++)a[i]=(1LL*P*a[i-1]+1LL*Q*i+R)%MOD; 10 for(h=1,t=A=B=0,i=n;i;i--){ 11 while(h<=t&&a[q[t]]<=a[i])t--; 12 q[++t]=i; 13 if(i+m-1<=n){ 14 while(q[h]>=i+m)h++; 15 A+=i^a[q[h]]; 16 B+=i^(t-h+1); 17 } 18 } 19 printf("%lld %lld\n",A,B); 20 } 21 }
6.2 树状数组
1 int lowbit(int i) 2 { 3 return i & -i;//求数组下标数的二进制的非0最低位所表示的值 4 } 5 void update(int i,int val)//更新单节点的值 6 { 7 while(i<=n){ 8 a[i]+=val; 9 i+=lowbit(i);//由叶子节点向上更新a数组 10 } 11 } 12 int sum(int i)//求和节点的值 13 { 14 int ret=0; 15 while(i>0){ 16 ret+=a[i];//从右往左区间求和 17 i-=lowbit(i); 18 } 19 return ret; 20 }
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cmath> 5 #include <cstring> 6 using namespace std; 7 int n,m,tree[2000010]; 8 int lowbit(int k) 9 { 10 return k & -k; 11 } 12 void add(int x,int k) 13 { 14 while(x<=n) 15 { 16 tree[x]+=k; 17 x+=lowbit(x); 18 } 19 } 20 int sum(int x) 21 { 22 int ans=0; 23 while(x!=0) 24 { 25 ans+=tree[x]; 26 x-=lowbit(x); 27 } 28 return ans; 29 } 30 int main() 31 { 32 cin>>n>>m; 33 for(int i=1;i<=n;i++) 34 { 35 int a; 36 scanf("%d",&a); 37 add(i,a); 38 } 39 for(int i=1;i<=m;i++) 40 { 41 int a,b,c; 42 scanf("%d%d%d",&a,&b,&c); 43 if(a==1) 44 add(b,c); 45 if(a==2) 46 cout<<sum(c)-sum(b-1)<<endl; 47 } 48 }
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <cmath> 6 #include <queue> 7 using namespace std; 8 int n,m; 9 int input[500010]; 10 int tree[500100]; 11 int lowbit(int x) 12 { 13 return x & -x; 14 } 15 void add(int x,int k) 16 { 17 while(x<=n) 18 { 19 tree[x]+=k; 20 x+=lowbit(x); 21 } 22 } 23 int search(int x) 24 { 25 int ans=0; 26 while(x!=0) 27 { 28 ans+=tree[x]; 29 x-=lowbit(x); 30 } 31 return ans; 32 } 33 int main() 34 { 35 cin>>n>>m; 36 for(int i=1;i<=n;i++) 37 cin>>input[i]; 38 for(int i=1;i<=m;i++) 39 { 40 int a; 41 scanf("%d",&a); 42 if(a==1) 43 { 44 int x,y,z; 45 scanf("%d%d%d",&x,&y,&z); 46 add(x,z); 47 add(y+1,-z); 48 } 49 if(a==2) 50 { 51 int x; 52 scanf("%d",&x); 53 printf("%d\n",input[x]+search(x)); 54 } 55 } 56 }
6.3 划分树 区间第k大(小)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 #define MAX_SIZE 100005 7 int sorted[MAX_SIZE];//已经排好序的数据 8 int toleft[25][MAX_SIZE]; 9 int tree[25][MAX_SIZE]; 10 void build_tree(int left, int right, int deep) 11 { 12 int i; 13 if (left == right) return ; 14 int mid = (left + right) >> 1; 15 int same = mid - left + 1; //位于左子树的数据 16 for (i = left; i <= right; ++i) {//计算放于左子树中与中位数相等的数字个数 17 if (tree[deep][i] < sorted[mid]) { 18 --same; 19 } 20 } 21 int ls = left; 22 int rs = mid + 1; 23 for (i = left; i <= right; ++i) { 24 int flag = 0; 25 if ((tree[deep][i] < sorted[mid]) || (tree[deep][i] == sorted[mid] && same > 0)) { 26 flag = 1; 27 tree[deep + 1][ls++] = tree[deep][i]; 28 if (tree[deep][i] == sorted[mid]) 29 same--; 30 } else { 31 tree[deep + 1][rs++] = tree[deep][i]; 32 } 33 toleft[deep][i] = toleft[deep][i - 1]+flag; 34 } 35 build_tree(left, mid, deep + 1); 36 build_tree(mid + 1, right, deep + 1); 37 } 38 int query(int left, int right, int k, int L, int R, int deep) 39 { 40 if (left == right) 41 return tree[deep][left]; 42 int mid = (L + R) >> 1; 43 int x = toleft[deep][left - 1] - toleft[deep][L - 1];//位于left左边的放于左子树中的数字个数 44 int y = toleft[deep][right] - toleft[deep][L - 1];//到right为止位于左子树的个数 45 int ry = right - L - y;//到right右边为止位于右子树的数字个数 46 int cnt = y - x;//[left,right]区间内放到左子树中的个数 47 int rx = left - L - x;//left左边放在右子树中的数字个数 48 if (cnt >= k) { 49 //printf("sss %d %d %d\n", xx++, x, y); 50 return query(L + x, L + y - 1, k, L, mid, deep + 1); 51 // 因为x不在区间内 所以没关系 所以先除去,从L+x开始,然后确定范围 52 } 53 else { 54 //printf("qqq %d %d %d\n", xx++, x, y); 55 return query(mid + rx + 1, mid + 1 + ry, k - cnt, mid + 1, R, deep + 1); 56 //同理 把不在区间内的 分到右子树的元素数目排除,确定范围 57 } 58 } 59 int main() 60 { 61 int m, n; 62 int a, b, k; 63 int i; 64 while (scanf("%d%d", &m, &n) == 2) { 65 for (i = 1; i <= m; ++i) { 66 scanf("%d", &sorted[i]); 67 tree[0][i] = sorted[i]; 68 } 69 sort(sorted + 1, sorted + 1 + m); 70 build_tree(1, m, 0); 71 for (i = 0; i < n; ++i) { 72 scanf("%d%d%d", &a, &b, &k); 73 printf("%d\n", query(a, b, k, 1, m, 0)); 74 } 75 } 76 return 0; 77 }
七.字符串
7.1 Manacher 最长回文子串
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 #include <queue> 7 #include <map> 8 using namespace std; 9 10 const int N = 200010; 11 char s[N], str[N*2]; 12 int p[N*2]; 13 int manacher() 14 { 15 int i; 16 for(i = 1; s[i]; i++) 17 str[i*2] = s[i], str[i*2+1] = '#'; 18 str[0] = '?', str[1] = '#', str[i*2] = '\0'; 19 int res = 0, k = 0, maxk = 0; 20 for(int i = 2; str[i]; i++) 21 { 22 p[i] = i < maxk ? min(maxk - i, p[2*k-i]) : 1; 23 while(str[i-p[i]] == str[i+p[i]]) p[i]++; 24 if(p[i] + i > maxk) 25 k = i, maxk = i + p[i]; 26 res = max(res, p[i]); 27 } 28 return res - 1; 29 } 30 31 int main() 32 { 33 while(~ scanf(" %s", s + 1)) 34 printf("%d\n", manacher()); 35 36 return 0; 37 }
7.2 最小/最大表示
1 int getMin(char *s) 2 { 3 int i = 0, j = 1, l; 4 int len = strlen(s); 5 while(i < len && j < len) 6 { 7 for(l = 0; l < len; l++) 8 if(s[(i + l) % len] != s[(j + l) % len]) break; 9 if(l >= len) break; 10 if(s[(i + l) % len] > s[(j + l) % len]) 11 { 12 if(i + l + 1 > j) i = i + l + 1; 13 else i = j + 1; 14 } 15 else if(j + l + 1 > i) j = j + l + 1; 16 else j = i + 1; 17 } 18 return i < j ? i : j; 19 } 20 21 int getMax(char *s) 22 { 23 int len = strlen(s); 24 int i = 0, j = 1, k = 0; 25 while(i < len && j < len && k < len) 26 { 27 int t = s[(i+k)%len]-s[(j+k)%len]; 28 if(!t) k++; 29 else 30 { 31 if(t > 0) 32 { 33 if(j+k+1 > i) j = j+k+1; 34 else j = i+1; 35 } 36 else if(i+k+1 > j) i = i+k+1; 37 else i = j+1; 38 k = 0; 39 } 40 } 41 return i < j ? i : j; 42 }
7.3 后缀数组
倍增法nlogn
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int MAX_N=1000010; 5 6 int n,k; 7 int rank[MAX_N]; 8 int tmp[MAX_N]; 9 int sa[MAX_N]; 10 //比较(rank[i],rank[i+k])和rank[j],rank[j+k] 11 bool compare_sa(int i,int j){ 12 if(rank[i]!=rank[j])return rank[i]<rank[j]; 13 else { 14 int ri=i+k<=n?rank[i+k]:-1; 15 int rj=j+k<=n?rank[j+k]:-1; 16 return ri<rj; 17 } 18 } 19 20 //计算字符串S的后缀数组 21 void construct_sa(string S,int *sa){ 22 n=S.length(); 23 24 //初始长度为1,rank直接截取字符编码 25 for(int i=0;i<=n;i++){ 26 sa[i]=i; 27 rank[i]=i<n?S[i]:-1; 28 } 29 30 //利用对长度为k的排序对长度为2k的排序 31 for(k=1;k<=n;k*=2){ 32 sort(sa,sa+n+1,compare_sa); 33 34 //先在tmp中临时存储新计算的rank,再转存回rank中 35 tmp[sa[0]]=0; 36 for(int i=1;i<=n;i++){ 37 tmp[sa[i]]=tmp[sa[i-1]] + (compare_sa(sa[i-1],sa[i]) ? 1 : 0) ; 38 } 39 for(int i=0;i<=n;i++){ 40 rank[i]=tmp[i]; 41 } 42 } 43 } 44 45 int main(){ 46 string x; 47 cin >> x; 48 construct_sa(x,sa); 49 for(int i=1;i<=x.size();i++){ 50 printf("%d ",sa[i]+1); 51 } 52 printf("\n"); 53 }
加桶不知道为什么就快了很多的倍增法
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #define inv inline void 5 #define maxN 1000010 6 using namespace std; 7 char s[maxN]; 8 //s是字符串的读入 9 int a[maxN],rk[maxN],sa[maxN],tp[maxN],tax[maxN],n,m; 10 //a是暂存数组,rk(第一关键字)第i位的排名,sa是排名为i的位置,tp是第二关键字辅助用的,tax是桶数组; 11 inv read(){ 12 scanf("%s",&s); 13 n=strlen(s); 14 for(int i=0;i<n;i++) a[i+1]=s[i]; 15 //读入 16 } 17 inv Rsort(){ 18 for(int i=0;i<=m;i++) tax[i]=0; 19 //tax是桶 20 //tax清零 21 for(int i=1;i<=n;i++) tax[rk[tp[i]]]++; 22 //每一个出现的第一关键字++ 23 for(int i=1;i<=m;i++) tax[i]+=tax[i-1]; 24 //tax中i现在代表这个数至多能排第几位 25 for(int i=n;i>=1;i--) sa[tax[rk[tp[i]]]--]=tp[i]; 26 //如果是第一遍就先不看这个 27 //就是把这一轮的sa做出来 28 //第二轮开始因为tp是按第二关键字进数组的 29 //所以从后往前来第二关键字肯定比前面小 30 //所以第一关键字相同时,第二关键字越大排名越后 31 //所以先拿到最大的值然后-- 32 } 33 inv Suffix(){ 34 for(int i=1;i<=n;i++) rk[i]=a[i],tp[i]=i; 35 //因为是第一轮所以直接用ascii码和位置当关键字; 36 Rsort(); 37 // for(int i=1;i<=n;i++) printf("%d ",sa[i]); 38 for(int k=1;k<=n;k<<=1){ 39 int num; 40 num=0; 41 for(int i=n-k+1;i<=n;i++) tp[++num]=i; 42 //从n-k+1开始,到n的位置的第二关键字都为零,所以先入数组 43 for(int i=1;i<=n;i++) if(sa[i]>k) tp[++num]=sa[i]-k; 44 //因为sa是排好序的,当sa[i]这个位置大于k时 45 //sa[i]就会作为别人的第二关键字,因为sa排好序的所以从小往大一个for就ok 46 //所以就把sa[i]-k这个位置先进 47 //上面这两个for做完后tp就完成了 48 Rsort(); 49 //再进行一次基数排序 50 swap(rk,tp); 51 //用tp存下这一轮rk 52 //下面开始更新下一轮rk 53 rk[sa[1]]=1; 54 //sa[1]的rk就是1 55 num=1; 56 //计数器&&自带更新排名 57 for(int i=2;i<=n;i++) 58 rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+k]==tp[sa[i-1]+k])?num:++num; 59 //()里面的是比较第一和第二关键字是否相同 60 //()里面的如果成立,就直接等于num 61 //如果不成立就++num并变成num++ 62 if(num==n) break; 63 //如果有n种排名就done了! 64 m=num; 65 //m代表tp的种类 66 } 67 return; 68 } 69 int main(){ 70 freopen("data.in","r",stdin); 71 freopen("data.out","w",stdout); 72 73 read(); 74 m=122; 75 Suffix(); 76 for(int i=1;i<=n;i++) printf("%d ",sa[i]); 77 // printf("\n"); 78 // for(int i=1;i<=n;i++) printf("%d ",rk[i]); 79 }
DC3
1 /* 2 suffix数组:第i位到最后的字符串 3 sa数组:将排序后的后缀的开头位置顺次放入SA中,称为后缀数组 4 rank数组:令rank[i]保存suffix[i]在排序中的名次,名次数组 5 */ 6 #include "stdio.h" 7 #include "string.h" 8 #define maxn 20004 9 #define maxm 1000005 10 11 #define F(x) ((x)/3+((x)%3==1?0:tb)) 12 #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2) 13 int wa[maxn],wb[maxn],wv[maxn],ws[maxm]; 14 int c0(int *r,int a,int b) 15 {return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];} 16 int c12(int k,int *r,int a,int b) 17 {if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1); 18 else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];} 19 void sort(int *r,int *a,int *b,int n,int m) 20 { 21 int i; 22 for(i=0;i<n;i++) wv[i]=r[a[i]]; 23 for(i=0;i<m;i++) ws[i]=0; 24 for(i=0;i<n;i++) ws[wv[i]]++; 25 for(i=1;i<m;i++) ws[i]+=ws[i-1]; 26 for(i=n-1;i>=0;i--) b[--ws[wv[i]]]=a[i]; 27 return; 28 } 29 void dc3(int *r,int *sa,int n,int m) 30 { 31 int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p; 32 r[n]=r[n+1]=0; 33 for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i; 34 sort(r+2,wa,wb,tbc,m); 35 sort(r+1,wb,wa,tbc,m); 36 sort(r,wa,wb,tbc,m); 37 for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++) 38 rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++; 39 if(p<tbc) dc3(rn,san,tbc,p); 40 else for(i=0;i<tbc;i++) san[rn[i]]=i; 41 for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3; 42 if(n%3==1) wb[ta++]=n-1; 43 sort(r,wb,wa,ta,m); 44 for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i; 45 for(i=0,j=0,p=0;i<ta && j<tbc;p++) 46 sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++]; 47 for(;i<ta;p++) sa[p]=wa[i++]; 48 for(;j<tbc;p++) sa[p]=wb[j++]; 49 return; 50 } 51 int rank[maxn],height[maxn]; 52 void calheight(int *r,int *sa,int n) 53 { 54 int i,j,k=0; 55 for(i=1;i<=n;i++) rank[sa[i]]=i; 56 for(i=0;i<n;height[rank[i++]]=k) 57 for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++); 58 return; 59 } 60 int RMQ[maxn]; 61 int mm[maxn]; 62 int best[20][maxn]; 63 void initRMQ(int n) 64 { 65 int i,j,a,b; 66 for(mm[0]=-1,i=1;i<=n;i++) 67 mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1]; 68 for(i=1;i<=n;i++) best[0][i]=i; 69 for(i=1;i<=mm[n];i++) 70 for(j=1;j<=n+1-(1<<i);j++) 71 { 72 a=best[i-1][j]; 73 b=best[i-1][j+(1<<(i-1))]; 74 if(RMQ[a]<RMQ[b]) best[i][j]=a; 75 else best[i][j]=b; 76 } 77 return; 78 } 79 int askRMQ(int a,int b) 80 { 81 int t; 82 t=mm[b-a+1];b-=(1<<t)-1; 83 a=best[t][a];b=best[t][b]; 84 return RMQ[a]<RMQ[b]?a:b; 85 } 86 int lcp(int a,int b)//从头开始比较a和b的对应字符持续相等的最远值 87 { 88 int t; 89 a=rank[a];b=rank[b]; 90 if(a>b) {t=a;a=b;b=t;} 91 return(height[askRMQ(a+1,b)]); 92 } 93 /* 94 */ 95 char st[maxn]; 96 int r[maxn*3],sa[maxn*3]; 97 /*求最长回文长 98 int main() 99 { 100 int i,n,len,k,ans=0,w; 101 scanf("%s",st); 102 len=strlen(st); 103 for(i=0;i<len;i++) r[i]=st[i]; 104 r[len]=1; 105 for(i=0;i<len;i++) r[i+len+1]=st[len-1-i]; 106 n=len+len+1; 107 r[n]=0; 108 dc3(r,sa,n+1,128); 109 calheight(r,sa,n); 110 for(i=1;i<=n;i++) RMQ[i]=height[i]; 111 initRMQ(n); 112 for(i=0;i<len;i++) 113 { 114 k=lcp(i,n-i); 115 if(k*2>ans) ans=k*2,w=i-k; 116 k=lcp(i,n-i-1); 117 if(k*2-1>ans) ans=k*2-1,w=i-k+1; 118 } 119 st[w+ans]=0; 120 printf("%s\n",st+w); 121 return 0; 122 }*/ 123 /*求最大公共字串长 124 int main() 125 { 126 int i,j,n,ans=0; 127 scanf("%s",st); 128 j=strlen(st); 129 st[j]=1; 130 scanf("%s",st+j+1); 131 n=strlen(st); 132 for(i=0;i<n;i++) r[i]=st[i]; 133 r[n]=0; 134 dc3(r,sa,n+1,128); 135 calheight(r,sa,n); 136 for(i=2;i<=n;i++) 137 if(height[i]>ans) 138 if((j<sa[i-1] && j>sa[i]) 139 || (j>sa[i-1] && j<sa[i])) ans=height[i]; 140 printf("%d\n",ans); 141 return 0; 142 } 143 */ 144 /*不可重叠最长重复子串 145 POJ 1743 最长重复子串(不可重叠) 146 题意: 147 有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题。“主题”是整个音符序列的一个子串,它需要满足如下条件: 148 1.长度至少为5个音符 149 2.在乐曲中重复出现(可能经过转调,“转调”的意思是主题序列中每个音符都被加上或减去了同一个整数值。) 150 3.重复出现的同一主题不能有公共部分。 151 int check(int *sa,int n,int k) 152 { 153 int i,max=sa[1],min=sa[1]; 154 for(i=2;i<=n;i++) 155 { 156 if(height[i]<k) max=min=sa[i]; 157 else 158 { 159 if(sa[i]<min) min=sa[i]; 160 if(sa[i]>max) max=sa[i]; 161 if(max-min>k) return(1); 162 } 163 } 164 return(0); 165 } 166 int main() 167 { 168 int i,j=0,k,n; 169 int min,mid,max; 170 scanf("%d",&n); 171 while(n!=0) 172 { 173 n--; 174 for(i=0;i<n;i++) 175 scanf("%d",&r[i]); 176 for(i=0;i<n;i++) 177 r[i]=r[i]-r[i+1]+90; 178 r[n]=0; 179 dc3(r,sa,n+1,200); 180 calheight(r,sa,n); 181 min=1;max=n/2; 182 while(min<=max) 183 { 184 mid=(min+max)/2; 185 if(check(sa,n,mid)) min=mid+1; 186 else max=mid-1; 187 } 188 if(max>=4) printf("%d\n",max+1); 189 else printf("0\n"); 190 scanf("%d",&n); 191 } 192 return 0; 193 } 194 */ 195 /* 196 可重叠的 k 次最长重复子串 197 给定一个长度为n的整数序列,求其中至少出现k次的子序列长度最长为多长 198 int check(int n,int k,int mid) 199 { 200 int i,s=1; 201 for(i=1;i<=n;i++) 202 if(height[i]>=mid) 203 { 204 s++; 205 if(s>=k) return(1); 206 } 207 else s=1; 208 return(0); 209 } 210 int main() 211 { 212 int i,k,n; 213 int min,mid,max; 214 scanf("%d %d",&n,&k); 215 for(i=0;i<n;i++) 216 { 217 scanf("%d",&r[i]); 218 r[i]++; 219 } 220 r[n]=0; 221 dc3(r,sa,n+1,1000002); 222 calheight(r,sa,n); 223 min=1;max=n; 224 while(min<=max) 225 { 226 mid=(min+max)/2; 227 if(check(n,k,mid)) min=mid+1; 228 else max=mid-1; 229 } 230 printf("%d\n",max); 231 return 0; 232 } 233 */ 234 235 /* 236 不相同的连续子串的个数 237 int main() 238 { 239 int i,n,t,ans; 240 scanf("%d",&t); 241 while(t-->0) 242 { 243 scanf("%s",st); 244 n=strlen(st); 245 for(i=0;i<n;i++) r[i]=st[i]; 246 r[n]=0; 247 dc3(r,sa,n+1,128); 248 calheight(r,sa,n); 249 ans=n*(n+1)/2; 250 for(i=1;i<=n;i++) ans-=height[i]; 251 printf("%d\n",ans); 252 } 253 return 0; 254 } 255 */ 256 /* 257 长度不小于 k 的公共子串的个数 258 int f[maxn]; 259 int a[maxn],b[maxn],c; 260 long long ss,ans; 261 int main() 262 { 263 int i,k,l,n,t; 264 scanf("%d",&k); 265 while(k!=0) 266 { 267 scanf("%s",st); 268 l=strlen(st); 269 st[l]=1; 270 scanf("%s",st+l+1); 271 n=strlen(st); 272 for(i=0;i<n;i++) r[i]=st[i]; 273 r[n]=0; 274 dc3(r,sa,n+1,128); 275 calheight(r,sa,n); 276 for(i=2;i<=n;i++) 277 { 278 f[i]=sa[i]<l; 279 height[i]-=k-1; 280 if(height[i]<0) height[i]=0; 281 } 282 height[n+1]=0; 283 a[0]=-1;ans=0; 284 for(t=0;t<=1;t++) 285 for(c=0,ss=0,i=2;i<=n;i++) 286 { 287 if(f[i]!=t) ans+=ss; 288 c++; 289 a[c]=height[i+1]; 290 b[c]=f[i]==t; 291 ss+=(long long)a[c]*b[c]; 292 while(a[c-1]>=a[c]) 293 { 294 ss-=(long long)(a[c-1]-a[c])*b[c-1]; 295 a[c-1]=a[c]; 296 b[c-1]+=b[c]; 297 c--; 298 } 299 } 300 printf("%I64d\n",ans); 301 scanf("%d",&k); 302 } 303 return 0; 304 } 305 */ 306
7.4 KMP
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int maxn=1e6+7; 5 int Next[maxn]; 6 //x表示模式串,Next保存以i为结尾的最长公共前缀和后缀的起始坐标 7 void get_next(string x){ 8 int i,j; 9 Next[0]=j=-1; 10 i=0; 11 while(i<x.length()){ 12 while(j!=-1&&x[j]!=x[i])j=Next[j]; 13 Next[++i]=++j; 14 } 15 } 16 //返回第一次匹配下标,x1匹配串,x2模式串 17 int kmp_id(string x1,string x2){ 18 int i,j; 19 i=j=0; 20 while(i<x1.length()&&j<x2.length()){ 21 while(j!=-1&&x1[i]!=x2[j]){ 22 j=Next[j]; 23 } 24 i++; 25 j++; 26 } 27 if(j==x2.length()) 28 return i-j; 29 return -1; 30 } 31 32 //返回匹配次数,x1匹配串,x2模式串 33 int kmp_ans(string x1,string x2){ 34 int i,j; 35 i=j=0; 36 int ans=0; 37 while(i<x1.length()){ 38 while(j!=-1&&x1[i]!=x2[j]){ 39 j=Next[j]; 40 } 41 if(j==x2.length()-1){ 42 ans++; 43 j=Next[j]; 44 } 45 i++; 46 j++; 47 } 48 return ans; 49 } 50 51 52 int main() 53 { 54 string x,y; 55 cin >> x; 56 get_next(x); 57 cin >>y; 58 cout << kmp_ans(y,x) << endl; 59 cout << kmp_id(y,x) << endl; 60 61 return 0; 62 }
1 void get_next() 2 {//next数组保存了以i结尾的字符串的最长公共前缀和后缀的起始坐标 3 int i,j; 4 next[0] = j = -1; 5 i = 0; 6 while(i < l2) 7 { 8 while(j!=-1&&str2[j]!=str2[i])//自身和自身进行匹配 9 j = next[j]; 10 next[++i] = ++j; 11 } 12 } 13 int kmp() 14 { 15 int i,j; 16 i = j = 0; 17 while(i < l1&&j<l2) 18 { 19 while(j!=-1&&str1[i]!=str2[j]) 20 { 21 j = next[j]; 22 } 23 i++; 24 j++; 25 26 } 27 if(j == l2) 28 return i-j;//完全匹配时的开始下标,下标从0开始 29 return -1;//不存在匹配情况 30 } 31 int kmp() 32 { 33 int i,j; 34 i = j = 0; 35 while(i < l1)//注意和返回下标的区别 36 { 37 while(j!=-1&&str1[i]!=str2[j]) 38 { 39 j = next[j]; 40 } 41 if(j == l2-1) 42 { 43 ans ++; 44 j = next[j]; 45 } 46 i++; 47 j++; 48 } 49 return ans;//返回匹配次数 50 }
1 void getnext( char T[],int len) 2 { 3 int i = 0, j = -1; 4 Next[0] = -1; 5 while(i < len) 6 { 7 if(j == -1 || T[i] == T[j]) 8 { 9 i++,j++; 10 Next[i] = j; 11 } 12 else 13 j = Next[j]; 14 } 15 } 16 17 void KMP(char s1[], char s2[], int len1, int len2 )//原串长度, 模式串长度 18 { 19 int i = 0, j = 0 ; 20 Next[0] = -1 ; 21 while( i < len1 && j < len2 ) 22 { 23 if( j == -1 || s1[i] == s2[j]) 24 { 25 i++; 26 j++; 27 } 28 else 29 j = Next[j]; 30 if( j == len2) 31 { 32 if(i < len1) 33 { 34 ans = min(ans, len1 - i); 35 } 36 j = Next[j]; 37 } 38 } 39 }
7.5 字典树
1 #include <cstring> 2 #include <vector> 3 #include <cstdio> 4 using namespace std; 5 //*********************************************************************************************** 6 const int maxnode = 4000 * 100 + 10; 7 const int sigma_size = 26; 8 9 // 字母表为全体小写字母的Trie 10 struct Trie { 11 int ch[maxnode][sigma_size]; 12 int val[maxnode]; 13 int sz; // 结点总数 14 void clear() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); } // 初始时只有一个根结点 15 int idx(char c) { return c - 'a'; } // 字符c的编号 16 17 // 插入字符串s,附加信息为v。注意v必须非0,因为0代表“本结点不是单词结点” 18 void insert(const char *s, int v) { 19 int u = 0, n = strlen(s); 20 for(int i = 0; i < n; i++) { 21 int c = idx(s[i]); 22 if(!ch[u][c]) { // 结点不存在 23 memset(ch[sz], 0, sizeof(ch[sz])); 24 val[sz] = 0; // 中间结点的附加信息为0 25 ch[u][c] = sz++; // 新建结点 26 } 27 u = ch[u][c]; // 往下走 28 } 29 val[u] = v; // 字符串的最后一个字符的附加信息为v 30 } 31 32 // 找字符串s的长度不超过len的前缀 33 bool find(const char *s, int len) { 34 int u = 0; 35 for(int i = 0; i < len; i++) { 36 if(s[i] == '\0') break; 37 int c = idx(s[i]); 38 if(!ch[u][c]) break; 39 u = ch[u][c]; 40 if(val[u] != 0) return true; // 找到一个前缀 41 } 42 return false; 43 } 44 }; 45 46 //***************************************************************************************************** 47 //以下为模板测试 48 Trie trie; 49 const int maxl = 300000 + 10; // 文本串最大长度 50 const int maxw = 4000 + 10; // 单词最大个数 51 const int maxwl = 100 + 10; // 每个单词最大长度 52 char text[maxl], word[maxwl]; 53 int main() 54 { 55 int n,m; 56 scanf("%d",&n); 57 trie.clear(); 58 while(n--){ 59 scanf("%s",word); 60 trie.insert(word,1); 61 } 62 scanf("%d",&m); 63 while(m--){ 64 scanf("%s",text); 65 int l=strlen(text); 66 if(trie.find(text,l)) printf("\"%s\" in it\n",text); 67 else printf("\"%s\" not in it\n",text); 68 } 69 return 0; 70 }
1 //Code highlighting produced by Actipro CodeHighlighter //(freeware)http://www.CodeHighlighter.com/--> 2 void createTrie(char *str) 3 { 4 int len = strlen(str); 5 Trie *p = root, *q; 6 for(int i=0; i<len; ++i) 7 { 8 int id = str[i]-'0'; 9 if(p->next[id] == NULL) 10 { 11 q = (Trie *)malloc(sizeof(Trie)); 12 q->v = 1; //初始v==1 13 for(int j=0; j<MAX; ++j) 14 q->next[j] = NULL; 15 p->next[id] = q; 16 p = p->next[id]; 17 } 18 else 19 { 20 p->next[id]->v++; 21 p = p->next[id]; 22 } 23 } 24 p->v = -1; //若为结尾,则将v改成-1表示 25 } 26 27 //Code highlighting produced by Actipro CodeHighlighter //(freeware)http://www.CodeHighlighter.com/--> 28 int findTrie(char *str) 29 { 30 int len = strlen(str); 31 Trie *p = root; 32 for(int i=0; i<len; ++i) 33 { 34 int id = str[i]-'0'; 35 p = p->next[id]; 36 if(p == NULL) //若为空集,表示不存以此为前缀的串 37 return 0; 38 if(p->v == -1) //字符集中已有串是此串的前缀 39 return -1; 40 } 41 return -1; //此串是字符集中某串的前缀 42 } 43 44 //Code highlighting produced by Actipro CodeHighlighter //(freeware)http://www.CodeHighlighter.com/--> 45 int dealTrie(Trie* T) 46 { 47 int i; 48 if(T==NULL) 49 return 0; 50 for(i=0;i<MAX;i++) 51 { 52 if(T->next[i]!=NULL) 53 deal(T->next[i]); 54 } 55 free(T); 56 return 0; 57 }
1 #include <cstring> 2 #include <vector> 3 #include <cstdio> 4 using namespace std; 5 //*********************************************************************************************** 6 const int maxnode = 4000 * 100 + 10; 7 const int sigma_size = 26; 8 9 // 字母表为全体小写字母的Trie 10 struct Trie { 11 int ch[maxnode][sigma_size]; 12 int val[maxnode]; 13 int sz; // 结点总数 14 void clear() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); } // 初始时只有一个根结点 15 int idx(char c) { return c - 'a'; } // 字符c的编号 16 17 // 插入字符串s,附加信息为v。注意v必须非0,因为0代表“本结点不是单词结点” 18 void insert(const char *s, int v) { 19 int u = 0, n = strlen(s); 20 for(int i = 0; i < n; i++) { 21 int c = idx(s[i]); 22 if(!ch[u][c]) { // 结点不存在 23 memset(ch[sz], 0, sizeof(ch[sz])); 24 val[sz] = 0; // 中间结点的附加信息为0 25 ch[u][c] = sz++; // 新建结点 26 } 27 u = ch[u][c]; // 往下走 28 } 29 val[u] = v; // 字符串的最后一个字符的附加信息为v 30 } 31 32 // 找字符串s的长度不超过len的前缀 33 bool find(const char *s, int len) { 34 int u = 0; 35 for(int i = 0; i < len; i++) { 36 if(s[i] == '\0') break; 37 int c = idx(s[i]); 38 if(!ch[u][c]) break; 39 u = ch[u][c]; 40 if(val[u] != 0) return true; // 找到一个前缀 41 } 42 return false; 43 } 44 }; 45 46 //***************************************************************************************************** 47 //以下为模板测试 48 Trie trie; 49 const int maxl = 300000 + 10; // 文本串最大长度 50 const int maxw = 4000 + 10; // 单词最大个数 51 const int maxwl = 100 + 10; // 每个单词最大长度 52 char text[maxl], word[maxwl]; 53 int main() 54 { 55 int n,m; 56 scanf("%d",&n); 57 trie.clear(); 58 while(n--){ 59 scanf("%s",word); 60 trie.insert(word,1); 61 } 62 scanf("%d",&m); 63 while(m--){ 64 scanf("%s",text); 65 int l=strlen(text); 66 if(trie.find(text,l)) printf("\"%s\" in it\n",text); 67 else printf("\"%s\" not in it\n",text); 68 } 69 return 0; 70 }
7.6 AC自动机
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <queue> 5 6 using namespace std; 7 8 const int maxn=550000; 9 10 struct AC_auto 11 { 12 //chd子节点 v表示字符长度,f表示失配指针, 13 int chd[maxn][26],v[maxn],f[maxn],last[maxn],sz,ans; 14 void init() 15 { 16 sz=1;ans=0; 17 memset(v,0,sizeof(v)); 18 memset(f,0,sizeof(f)); 19 memset(chd[0],0,sizeof(chd[0])); 20 } 21 void insert(char* p) 22 { 23 int cur=0; 24 int tmp=strlen(p); 25 for(;*p;p++) 26 { 27 if(!chd[cur][*p-'a']) 28 { 29 memset(chd[sz],0,sizeof(chd[sz])); 30 chd[cur][*p-'a']=sz++; 31 } 32 cur=chd[cur][*p-'a']; 33 } 34 v[cur]=tmp; 35 } 36 bool query(char* p) 37 { 38 int cur=0; 39 for(;*p;p++) 40 { 41 if(!chd[cur][*p-'a']) break; 42 cur=chd[cur][*p-'a']; 43 } 44 return v[cur]&&(!(*p)); 45 } 46 int getFail() 47 { 48 queue<int> q; 49 f[0]=0; 50 for(int c=0;c<26;c++) 51 { 52 int u=chd[0][c]; 53 if(u) 54 { 55 f[u]=0; q.push(u); last[u]=0; 56 } 57 } 58 while(!q.empty()) 59 { 60 int r=q.front(); q.pop(); 61 for(int c=0;c<26;c++) 62 { 63 int u=chd[r][c]; 64 if(!u){ chd[r][c]=chd[f[r]][c];continue;}///.... 65 q.push(u); 66 int vv=f[r]; 67 while(vv&&!chd[vv][c]) vv=f[vv]; 68 f[u]=chd[vv][c]; 69 last[u]=v[f[u]] ? f[u] : last[f[u]]; 70 } 71 } 72 } 73 void solve(int j) 74 { 75 if(!j) return; 76 if(v[j]) 77 { 78 ans+=v[j]; 79 v[j]=0; 80 } 81 solve(last[j]); 82 } 83 //找出现单词(不计次数) 84 void find(char* T) 85 { 86 int n=strlen(T),j=0; 87 getFail(); 88 for(int i=0;i<n;i++) 89 { 90 //while(j&&!chd[j][*T-'a']) j=f[j]; 91 j=chd[j][T[i]-'a']; 92 if(v[j]) solve(j); 93 else if(last[j]) solve(last[j]); 94 } 95 } 96 //替换最长匹配前缀为'*'; 97 void find1(char* T) 98 { 99 int n=strlen(T),j=0; 100 getFail(); 101 for(int i=0; i<n; i++) 102 { 103 // if(!(str[i]>='a'&&str[i]<='z'||str[i]>='A'&&str[i]<='Z'))continue; 104 105 106 j=chd[j][T[i]-'a']; 107 int temp=j; 108 while(temp!=0) 109 { 110 if(v[temp] != 0) 111 { 112 for(int k=i-v[temp]+1; k<=i; k++) 113 { 114 //str1[k]='*'; 115 } 116 break; 117 } 118 temp = f[temp]; 119 } 120 121 122 } 123 } 124 }ac; 125 126 int main() 127 { 128 int t,n; 129 char dic[100],str[1100000]; 130 scanf("%d",&t); 131 while(t--) 132 { 133 ac.init(); 134 scanf("%d",&n); 135 while(n--) 136 { 137 scanf("%s",dic); 138 ac.insert(dic); 139 } 140 scanf("%s",str); 141 ac.find(str); 142 printf("%d\n",ac.ans); 143 } 144 return 0; 145 }
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <queue> 5 6 using namespace std; 7 8 const int maxn=550000; 9 10 struct AC_auto 11 { 12 int chd[maxn][26],v[maxn],f[maxn],last[maxn],sz,ans; 13 void init() 14 { 15 sz=1;ans=0; 16 memset(v,0,sizeof(v)); 17 memset(f,0,sizeof(f)); 18 memset(chd[0],0,sizeof(chd[0])); 19 } 20 void insert(char* p) 21 { 22 int cur=0; 23 for(;*p;p++) 24 { 25 if(!chd[cur][*p-'a']) 26 { 27 memset(chd[sz],0,sizeof(chd[sz])); 28 chd[cur][*p-'a']=sz++; 29 } 30 cur=chd[cur][*p-'a']; 31 } 32 v[cur]++; 33 } 34 bool query(char* p) 35 { 36 int cur=0; 37 for(;*p;p++) 38 { 39 if(!chd[cur][*p-'a']) break; 40 cur=chd[cur][*p-'a']; 41 } 42 return v[cur]&&(!(*p)); 43 } 44 int getFail() 45 { 46 queue<int> q; 47 f[0]=0; 48 for(int c=0;c<26;c++) 49 { 50 int u=chd[0][c]; 51 if(u) 52 { 53 f[u]=0; q.push(u); last[u]=0; 54 } 55 } 56 while(!q.empty()) 57 { 58 int r=q.front(); q.pop(); 59 for(int c=0;c<26;c++) 60 { 61 int u=chd[r][c]; 62 if(!u){ chd[r][c]=chd[f[r]][c];continue;}///.... 63 q.push(u); 64 int vv=f[r]; 65 while(vv&&!chd[vv][c]) vv=f[vv]; 66 f[u]=chd[vv][c]; 67 last[u]=v[f[u]] ? f[u] : last[f[u]]; 68 } 69 } 70 } 71 void solve(int j) 72 { 73 if(!j) return; 74 if(v[j]) 75 { 76 ans+=v[j]; 77 v[j]=0; 78 } 79 solve(last[j]); 80 } 81 void find(char* T) 82 { 83 int n=strlen(T),j=0; 84 getFail(); 85 for(int i=0;i<n;i++) 86 { 87 //while(j&&!chd[j][*T-'a']) j=f[j]; 88 j=chd[j][T[i]-'a']; 89 if(v[j]) solve(j); 90 else if(last[j]) solve(last[j]); 91 } 92 } 93 }ac; 94 95 int main() 96 { 97 int t,n; 98 char dic[100],str[1100000]; 99 scanf("%d",&t); 100 while(t--) 101 { 102 ac.init(); 103 scanf("%d",&n); 104 while(n--) 105 { 106 scanf("%s",dic); 107 ac.insert(dic); 108 } 109 scanf("%s",str); 110 ac.find(str); 111 printf("%d\n",ac.ans); 112 } 113 return 0; 114 }
7.7 扩展KMP
1 #include <bits/stdc++.h> 2 using namespace std; 3 //const int maxn=1e5+10; 4 //int Next[maxn]; 5 //int ret[maxn]; 6 // 7 ////扩展kmp求a为模式串,b为文本串的,b的所有后缀对a的最长公共前缀 8 //void extendedKMP(char *a,char *b,int M,int N,int *Next,int *ret){ 9 // int i,j,k; 10 // for(j=0;1+j<M&&a[j]==a[1+j];j++); 11 // Next[1]=j; 12 // k=1; 13 // for(i=2;i<M;i++){ 14 // int Len=k+Next[k],L=Next[i-k]; 15 // if(L<Len-i){ 16 // Next[i]=L; 17 // }else { 18 // for(j=max(0,Len-i);i+j<M&&a[j]==a[i+j];j++); 19 // Next[i]=j; 20 // k=i; 21 // } 22 // } 23 // 24 // for(j=0;j<N&&j<M&&a[j]==b[j];j++); 25 // ret[0]=j; 26 // k=0; 27 // for(i=1;i<N;i++){ 28 // int Len=k+ret[k],L=Next[i-k]; 29 // if(L<Len-i){ 30 // ret[i]=L; 31 // }else { 32 // for(i=max(0,Len-i);j<M&&i+j<N&&a[j]==b[i+j];j++); 33 // ret[i]==j;k=i; 34 // } 35 // } 36 // 37 //} 38 const int maxn=100010; //字符串长度最大值 39 int next[maxn],ex[maxn]; //ex数组即为extend数组 40 //预处理计算next数组 41 void GETNEXT(char *str) 42 { 43 int i=0,j,po,len=strlen(str); 44 next[0]=len;//初始化next[0] 45 while(str[i]==str[i+1]&&i+1<len)//计算next[1] 46 i++; 47 next[1]=i; 48 po=1;//初始化po的位置 49 for(i=2;i<len;i++) 50 { 51 if(next[i-po]+i<next[po]+po)//第一种情况,可以直接得到next[i]的值 52 next[i]=next[i-po]; 53 else//第二种情况,要继续匹配才能得到next[i]的值 54 { 55 j=next[po]+po-i; 56 if(j<0)j=0;//如果i>po+next[po],则要从头开始匹配 57 while(i+j<len&&str[j]==str[j+i])//计算next[i] 58 j++; 59 next[i]=j; 60 po=i;//更新po的位置 61 } 62 } 63 } 64 //计算extend数组,s1为文本串,s2为模式串,求s1的所有后缀对s2的最长公共前缀 65 void EXKMP(char *s1,char *s2) 66 { 67 int i=0,j,po,len=strlen(s1),l2=strlen(s2); 68 GETNEXT(s2);//计算子串的next数组 69 while(s1[i]==s2[i]&&i<l2&&i<len)//计算ex[0] 70 i++; 71 ex[0]=i; 72 po=0;//初始化po的位置 73 for(i=1;i<len;i++) 74 { 75 if(next[i-po]+i<ex[po]+po)//第一种情况,直接可以得到ex[i]的值 76 ex[i]=next[i-po]; 77 else//第二种情况,要继续匹配才能得到ex[i]的值 78 { 79 j=ex[po]+po-i; 80 if(j<0)j=0;//如果i>ex[po]+po则要从头开始匹配 81 while(i+j<len&&j<l2&&s1[j+i]==s2[j])//计算ex[i] 82 j++; 83 ex[i]=j; 84 po=i;//更新po的位置 85 } 86 } 87 } 88 89 int main() 90 { 91 char *a="aaaabaa"; 92 char *b="aaaaaaa"; 93 EXKMP(a,b); 94 for(int i=0;i<7;i++){ 95 printf("%d %d\n",next[i],ex[i]); 96 } 97 }
7.8 华丽的hash
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 typedef unsigned long long ull; 6 ull base=131; 7 ull a[10010]; 8 char s[10010]; 9 int n,ans=1; 10 ull hashs(char s[]) 11 { 12 int len=strlen(s); 13 ull ans=0; 14 for (int i=0;i<len;i++) 15 ans=ans*base+(ull)s[i]; 16 return ans&0x7fffffff; 17 } 18 main() 19 { 20 scanf("%d",&n); 21 for (int i=1;i<=n;i++) 22 { 23 scanf("%s",s); 24 a[i]=hashs(s); 25 } 26 sort(a+1,a+n+1); 27 for (int i=2;i<=n;i++) 28 if (a[i]!=a[i-1]) 29 ans++; 30 printf("%d\n",ans); 31 } 32
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 typedef unsigned long long ull; 6 ull base=131; 7 ull a[10010]; 8 char s[10010]; 9 int n,ans=1; 10 ull mod=19260817; 11 ull hashs(char s[]) 12 { 13 int len=strlen(s); 14 ull ans=0; 15 for (int i=0;i<len;i++) 16 ans=(ans*base+(ull)s[i])%mod; 17 return ans; 18 } 19 main() 20 { 21 scanf("%d",&n); 22 for (int i=1;i<=n;i++) 23 { 24 scanf("%s",s); 25 a[i]=hashs(s); 26 } 27 sort(a+1,a+n+1); 28 for (int i=2;i<=n;i++) 29 if (a[i]!=a[i-1]) 30 ans++; 31 printf("%d\n",ans); 32 } 33
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 typedef unsigned long long ull; 6 ull base=131; 7 struct data 8 { 9 ull x,y; 10 }a[10010]; 11 char s[10010]; 12 int n,ans=1; 13 ull mod1=19260817; 14 ull mod2=19660813; 15 ull hash1(char s[]) 16 { 17 int len=strlen(s); 18 ull ans=0; 19 for (int i=0;i<len;i++) 20 ans=(ans*base+(ull)s[i])%mod1; 21 return ans; 22 } 23 ull hash2(char s[]) 24 { 25 int len=strlen(s); 26 ull ans=0; 27 for (int i=0;i<len;i++) 28 ans=(ans*base+(ull)s[i])%mod2; 29 return ans; 30 } 31 bool comp(data a,data b) 32 { 33 return a.x<b.x; 34 } 35 main() 36 { 37 scanf("%d",&n); 38 for (int i=1;i<=n;i++) 39 { 40 scanf("%s",s); 41 a[i].x=hash1(s); 42 a[i].y=hash2(s); 43 } 44 sort(a+1,a+n+1,comp); 45 for (int i=2;i<=n;i++) 46 if (a[i].x!=a[i-1].x || a[i-1].y!=a[i].y) 47 ans++; 48 printf("%d\n",ans); 49 }
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 typedef unsigned long long ull; 6 ull base=131; 7 ull a[10010]; 8 char s[10010]; 9 int n,ans=1; 10 ull mod=212370440130137957ll; 11 ull hashs(char s[]) 12 { 13 int len=strlen(s); 14 ull ans=0; 15 for (int i=0;i<len;i++) 16 ans=(ans*base+(ull)s[i])%mod; 17 return ans; 18 } 19 main() 20 { 21 scanf("%d",&n); 22 for (int i=1;i<=n;i++) 23 { 24 scanf("%s",s); 25 a[i]=hashs(s); 26 } 27 sort(a+1,a+n+1); 28 for (int i=2;i<=n;i++) 29 if (a[i]!=a[i-1]) 30 ans++; 31 printf("%d\n",ans); 32 }
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 typedef unsigned long long ull; 6 ull base=131; 7 ull po[100010],hs[100010*100]; 8 char s1[100010],s2[100010*100]; 9 int n,ans=1,T; 10 ull geth(int l,int r) 11 { 12 return (ull)hs[r]-po[r-l+1]*hs[l-1]; 13 } 14 main() 15 { 16 freopen("oulipo.in","r",stdin); 17 freopen("oulipo.out","w",stdout); 18 po[0]=1; 19 for (int i=1;i<=10010-5;i++) 20 po[i]=po[i-1]*base; 21 scanf("%d",&T); 22 while(T--) 23 { 24 scanf("%s%s",s1+1,s2+1); 25 int l1=strlen(s1+1),l2=strlen(s2+1); 26 ull a1=0,ans=0; 27 for (int i=1;i<=l1;i++) 28 a1=a1*base+(ull)s1[i]; 29 for (int i=1;i<=l2;i++) 30 hs[i]=hs[i-1]*base+s2[i]; 31 for (int i=1;i+l1-1<=l2;i++) 32 if (a1==geth(i,i+l1-1)) 33 ans++; 34 printf("%d\n",ans); 35 } 36 }
1 写到这里突然发现hash好像可以暴力水过很多字符串算法。。 2 3 1、kmp 4 5 问题:给两个字符串S1,S2,求S2是否是S1的子串,并求S2在S1中出现的次数 6 7 把S2 Hash出来,在S1里找所有长度为|S2||S2|的子串,Hash比较。效率O(|S1|)O(|S1|) 8 9 2、AC自动机 10 11 问题:给N个单词串,和一个文章串,求每个单词串是否是文章串的子串,并求每个单词在文章中出现的次数。 12 13 把每一个单词hash成整数,再把文章的每一个子串hash成整数,接下来只需要进行整数上的查找即可。 14 15 复杂度:O(|A|2+|S|)O(|A|2+|S|) 16 17 用AC自动机可以做到O(|A|+|S|)O(|A|+|S|)的复杂度,|S||S|是单词串总长,|A||A|是文章长度 18 19 3、后缀数组 20 21 问题:给两个字符串S1,S2,求它们的最长公共子串的长度。 22 23 将S1的每一个子串都hash成一个整数,将S2的每一个子串都hash成一个整数 24 25 两堆整数,相同的配对,并且找到所表示的字符串长度最大的即可。 26 27 复杂度:O(|S1|2+|S2|2)O(|S1|2+|S2|2) 28 29 用后缀数组可以优化到O(|S|log|S|)O(|S|log|S|) 30 31 4、马拉车 32 33 问题:给一个字符串S,求S的最长回文子串。 34 35 先求子串长度位奇数的,再求偶数的。枚举回文子串的中心位置,然后二分子串的长度,直到找到一个该位置的最长回文子串,不断维护长度最大值即可。 36 37 复杂度:O(|S|log|S|)O(|S|log|S|) 38 39 用manacher可以做到O(|S|)O(|S|)的复杂度 40 41 5、扩展kmp 42 43 问题:给一个字符串S,求S的每个后缀与S的最长公共前缀 44 45 枚举每一个后缀的起始位置,二分长度,求出每个后缀与S的最长公共前缀。 46 47 复杂度:O(|S|log|S|)O(|S|log|S|) 48 49 用extend-kmp可以做到O(|S|)
八、杂
8.1 三分求极大极小点
1 int SanFen(int l,int r) //找凸点 2 { 3 while(l < r-1) 4 { 5 int mid = (l+r)/2; 6 int mmid = (mid+r)/2; 7 if( f(mid) > f(mmid) ) 8 r = mmid; 9 else 10 l = mid; 11 } 12 return f(l) > f(r) ? l : r; 13 }
1 double three_devide(double low,double up) 2 { 3 double m1,m2; 4 while(up-low>=eps) 5 { 6 m1=low+(up-low)/3; 7 m2=up-(up-low)/3; 8 if(f(m1)<=f(m2)) 9 low=m1; 10 else 11 up=m2; 12 } 13 return (m1+m2)/2; 14 }
1 int SanFen(int l,int r) //找凸点 2 { 3 while(l < r-1) 4 { 5 int mid = (l+r)/2; 6 int mmid = (mid+r)/2; 7 if( f(mid) > f(mmid) ) 8 l = mid; 9 else 10 r = mmid; 11 } 12 return f(l) > f(r) ? l : r; 13 }
1 double three_devide(double low,double up) 2 { 3 double m1,m2; 4 while(up-low>=eps) 5 { 6 m1=low+(up-low)/3; 7 m2=up-(up-low)/3; 8 if(f(m1)<=f(m2)) 9 up=m2; 10 else 11 low=m1; 12 } 13 return (m1+m2)/2; 14 }
8.2 欧拉函数递推
1 void Euler(){ 2 phi[1] = 1; 3 for(int i = 2; i < N; i ++){ 4 if(!phi[i]){ 5 phi[i] = i-1; 6 prime[tot ++] = i; 7 } 8 for(int j = 0; j < tot && 1ll*i*prime[j] < N; j ++){ 9 if(i % prime[j]) phi[i * prime[j]] = phi[i] * (prime[j]-1); 10 else{ 11 phi[i * prime[j] ] = phi[i] * prime[j]; 12 break; 13 } 14 } 15 } 16 sum[1]=0; 17 for(int i=2;i<N/2;i++){ 18 sum[i]=phi[2*i]/2; 19 sum[i]=sum[i-1]+sum[i]; 20 } 21 }
posted on 2018-05-05 10:19 Best_Efforts 阅读(527) 评论(0) 编辑 收藏 举报