ACM模板
2018-03-30 19:52
337 查看
之前做过acm,总结出来了一些算法模板。这些是我在搞懂先自己写然后想大牛靠拢不断优化的结果,可能有些是大牛们的源代码,在此一并发出,希望对大家有所帮助,代码中可能有错,在此表示歉意。[cpp] view plain copy 动态规划模板
处理求矩阵的最大子矩
//************************************************************
//求a
[m]的最大子矩阵
///计算从某固定行开始起连续列b[i,j]动态规划求最大值
int dp( const int *b,int m)
{
int sum ,max,i;
sum=max=b[0];
for(i=1;i<m;i++)
{
if(sum>0)sum+=b[i];
else sum=b[i];
if(sum>max)max=sum;
}
return max;
}
//b[k]可以取到任意连续行的排列组合
max=-999999999;
for(i=0;i<n;i++)//控制具体哪一行开始
{
memset(b,0,sizeof(b));
for(j=i;j<n;j++)//表示从第i行到最后n行中间每列累加得b[k]
{
for(k=0;k<m;k++)
b[k]+=a[j][k];
if(dp(b,m)>max) max=dp(b,m);
}
}
//************************************************************
动态规划求最大面积
//************************************************************
{
首先开辟数组a
,l
,r
////找出其左右区间比其大的区间长度
l[1]=1;
r
=n;
for (i=2; i<=n; ++i)
{
t=i;
while (t>1 && a[i]<=a[t-1]) t=l[t-1];
l[i]=t;
}
for (i=n-1; i>=1; --i)
{
t=i;
while (t<n && a[i]<=a[t+1]) t=r[t+1];
r[i]=t;
}
/////分别以每个小矩形的高度为高求大矩形面积,并找出其最大者
max=0;
for (i=1; i<=n; ++i)
{
if ((r[i]-l[i]+1)*a[i]>max) max=(r[i]-l[i]+1)*a[i];
}
}
//************************************************************
最长公共子序列
//*********************************************************
//算法1:时间复杂度为O(N*M),空间复杂度为O(N*N)
const int MAXN=1000;
char str1[MAXN],str2[MAXN];
int dp[MAXN][MAXN];
int LCS1(char *str1,char *str2)
{
int len1=strlen(str1);
int len2=strlen(str2);
int i,j;
memset(dp,0,sizeof(dp));
for(i=1;i<=len1;i++)
{
for(j=1;j<=len2;j++)
{
if(str1[i-1]==str2[j-1])
dp[i][j]=dp[i-1][j-1]+1;
else if(dp[i-1][j]>dp[i][j-1])
dp[i][j]=dp[i-1][j];
else dp[i][j]=dp[i][j-1];
}
}
return dp[len1][len2];
}
//===============================================//算法2:时间复杂度为O(N*M),空间复杂度为O(N)
const int MAXN=1000;
char str1[MAXN],str2[MAXN];
int dp[2][MAXN];//采用滚动数组优化
int LCS2(char *str1,char *str2)
{
int len1=strlen(str1);
int len2=strlen(str2);
int i,j,k;
memset(dp,0,sizeof(dp));
for(i=1;i<=len1;i++)
{
k=i&1;
for(j=1;j<=len2;j++)
{
if(str1[i-1]==str2[j-1])
dp[k][j]=dp[k][j-1]+1;
else if(dp[k^1][j]>dp[k][j-1])
dp[k][j]=dp[k^1][j];
else dp[k][j]=dp[k][j-1];
}
}
return dp[k][len2];
}
//*********************************************************
最长公共递增子序列
//************************************************************
//其中a
,b
分别存放俩序列
memset(dp,0,sizeof(dp));
max=0;
for(i=0;i<n;i++)
{
k=0;
for(j=0;j<m;j++)
{
//////////////保证递增又保证找到最大
if(a[i]>b[j]&&dp[k]<dp[j])
k=j;
if(a[i]==b[j])
dp[j]=dp[k]+1;
if(max<dp[j])
max=dp[j];
}
}
//************************************************************
数塔问题
//************************************************************
//n,m分别表示行数列数
void shuta(int n,int m)
{
int i,j;
memset(data,0,sizeof(data));
memset(dp,0,sizeof(dp));
for(i=1;i<=n;i++)
{
for(j=1;j<=i;j++)
{
cin>>data[i][j];
}
}
for(j=1;j<=m;j++)
dp
[j]=data
[j];
for(i=n-1;i>=1;i--)
{
for(j=i;j>=1;j--)
{
dp[i][j]=max(dp[i+1][j]+data[i][j],dp[i+1][j+1]+data[i][j]);
}
}
}
//************************************************************
最长递增子序列
//求严格递增子序列的最长长度
//************************************************************
//算法1的时间复杂度为O(N*log(N))
const int MAXN=1000;
int data[MAXN];//存放原始数据
int MaxV[MAXN];//MaxV[i]存放长度为i的严格递增子序列的最大值的最小值
//二分查找返回MaxV中大于等于x的组靠左的下标
int BinaryResearch(int x,int len)
{
int mid,low=1,high=len;
while(low<=high)
{
mid=(low+high)>>1;
if(MaxV[mid]<x)
low=mid+1;
else high=mid-1;
}
return low;
}
//返回原序列中严格递增子序列的最长长度
int LIS2(int n)
{
int i,len=1;
MaxV[1]=data[0];
for(i=1;i<n;i++)
{
if(data[i]>MaxV[len])//比长度为len的子序列最大值大,直接加进末尾
MaxV[++len]=data[i];
else
{
int pos=BinaryResearch(data[i],len);
MaxV[pos]=data[i];
}
}
return len;
}
//===============================================
//算法2的时间复杂度为O(N*N)
int dp[MAXN];
//返回原序列中严格递增子序列的最长长度
int LIS1(int n)
{
int i,j,lmax;
lmax=0;
for(i=0;i<n;i++)
{
dp[i]=1;
for(j=0;j<i;j++)
{
if(data[i]>data[j]&&dp[j]+1>dp[i])
dp[i]=dp[j]+1;
}
if(dp[i]>lmax)lmax=dp[i];
}
return lmax;
}
//************************************************************
最大字段和
//************************************************************
void MSS(int n)
{
int i;
int max=NINF;
memset(dp,0,sizeof(dp));
for(i=1;i<=n;i++)
{
if(dp[i-1]<=0)
dp[i]=data[i];
else dp[i]=dp[i-1]+data[i];
if(max<dp[i])
max=dp[i];
}
cout<<max<<endl;
}
//************************************************************
多重背包
//************************************************************
int dp[100000+10];//视具体情况而定
struct node
{
int num,weight,value;//分别代表每种物品的数量、重量、价值
}Good[15];//定义每个物品的结构体
int Max_weight;//背包的载重量
int max(int a,int b)
<
ad8cd
li style="list-style-position:outside;border-style:none none none solid;border-left-width:3px;border-left-color:rgb(108,226,108);line-height:18px;background-color:rgb(248,248,248);margin-right:0px;margin-bottom:0px;padding:0px 3px 0px 10px;">{
return a<b?b:a;
}
void ZeroOnePack(int weight,int value,int lim)
//对应01背包的求法
{
for(int i=lim;i>=weight;i--)
dp[i]=max(dp[i],dp[i-weight]+value);
}
void CompletePack(int weight,int value,int lim)
//对应完全背包的求法
{
for(int i=weight;i<=lim;i++)
dp[i]=max(dp[i],dp[i-weight]+value);
}
void MultiplePack(int weight,int value,int amount,int lim)
//选定物品后的多重背包的求法
{
if(weight*amount>=lim)CompletePack(weight,value,lim);
else
{
for(int k=1;k<amount;)
{
ZeroOnePack(k*weight,k*value,lim);
amount-=k;
k<<=1;
}
ZeroOnePack(amount*weight,amount*value,lim);
}
}
void Solve(int n)
//解决物品种类数为n的多重背包问题求法
{
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
MultiplePack(Good[i].weight,Good[i].value,Good[i].num,Max_weight);
}
//************************************************************
数据结构
单调队列
const int MAXN=100000+10;
int da[MAXN],Inc[MAXN],Dec[MAXN];
int n,m,k,front1,rear1,front2,rear2;
//维护双端单调队列,求da[]中满足m<=Max-Min<=k最长连续子序列
int Queue()
{
front1=0,rear1=-1,front2=0,rear2=-1;
int i,ans=0,start=0;
for(i=0;i<n;i++)
{
while(front1<=rear1&&da[Dec[rear1]]<=da[i])//保证Dec队列在start-i区间内的递减
rear1--;
Dec[++rear1]=i;
while(front2<=rear2&&da[Inc[rear2]]>=da[i])//保证inc队列在start-i区间内的递增
rear2--;
Inc[++rear2]=i;
while(da[Dec[front1]]-da[Inc[front2]]>k)
{
//保证区间左端点向后滑动一个长度
if(Dec[front1]-Inc[front2]<0)
{
start=Dec[front1]+1;
front1++;
}
else
{
start=Inc[front2]+1;
front2++;
}
}
//满足m<=Max-Min<=k
if(da[Dec[front1]]-da[Inc[front2]]>=m)
{
if(i-start+1>ans)
ans=i-start+1;
}
}
return ans;
}
并查集
//*****************************************************************
1.求父亲节点并压缩路径
int par[MAX];
int Get_par(int a)
//查找a的父亲节点并压缩路径
{
if(par[a]==a)
return par[a];
//注意语句的顺序
int pa=par[a];
par[a]=Get_par(par[a]);
//
return par[a];
}
2.合并
void Merge(int a,int b)
//合并a,b
{
int pa,pb;
pa=Get_par(a);
pb=Get_par(b);
par[pa]=pb;
}
//*****************************************************************
线段树
不带延迟更新的操作
//************************************************************
int da[MAXN];
struct node
{
int left,right,sum;//sum此处灵活处理
}tree[MAXN*4];
//1.建立以left,right为左右边界,将数组da中元素存储在首地址从1开始的线段树tree的叶节点上
void Build( int id,int left,int right)
{
tree[id].left=left;
tree[id].right=right;
if(left==right)
{
//tree[id].sum=da[left];//此处可以直接初始化为对应da[left]
return ;
}
else
{
int mid =(left+right)>>1;
Build(id<<1,left,mid);
Build((id<<1)|1,mid+1,right);
//tree[id].sum=tree[(id<<1)].sum+tree[(id<<1)|1].sum;
}
}
//2.在线段树的叶节点pos处加val
void Updata(int id,int pos,int val)
{
tree[id].sum+=val;
if(tree[id].left==tree[id].right&&tree[id].left==pos)
{
return ;
}
int mid=(tree[id].left+tree[id].right)>>1;
if(pos<=mid)
Updata(id<<1,pos,val);
else
Updata((id<<1)|1,pos,val);
}
//3.查询区间[left,right]上的和
int Query(int id,int left,int right)
{
if(tree[id].left==left&&tree[id].right==right)
{
return tree[id].sum;
}
int mid=(tree[id].left+tree[id].right)>>1;
if(right<=mid)
return Query(id<<1,left,right);
if(left>=mid+1)
return Query((id<<1)|1,left,right);
return Query(id<<1,left,mid)+Query((id<<1)|1,mid+1,right);
}
//************************************************************
线段树的延迟更新
//*****************************************************
const int MAXN=100000+100;
struct node
{
int left,right,add;
int Max;
}tree[MAXN*4];
void build(int id, int left, int right)
{
tree[id].add=0;
tree[id].left=left;
tree[id].right=right;
tree[id].Max=0;
if(left==right)
{
return ;
}
else
{
int mid=(left+right)>>1;
build(id<<1,left,mid);
build((id<<1)|1,mid+1,right);
}
}
void updata(int id,int left,int right,int adi)
{
if(tree[id].left==left&&tree[id].right==right)
{
tree[id].add+=adi;
return ;
}
int mid=(tree[id].left+tree[id].right)>>1;
if(right<=mid)
updata(id<<1,left,right,adi);
else if(left>mid)
updata((id<<1)|1,left,right,adi);
else
{
updata(id<<1,left,mid,adi);
updata((id<<1)|1,mid+1,right,adi);
}
tree[id].Max=max(tree[id<<1].Max+tree[id<<1].add,tree[(id<<1)|1].Max+tree[(id<<1)|1].add);
}
int query(int id,int left,int right)
{
if(tree[id].left==left&&tree[id].right==right)
{
return tree[id].Max+tree[id].add;
}
int mid=(tree[id].left+tree[id].right)>>1;
if(tree[id].add!=0)
{
updata(id<<1,tree[id].left,mid,tree[id].add);
updata((id<<1)|1,mid+1,tree[id].right,tree[id].add);
tree[id].Max=max(tree[id<<1].Max+tree[id<<1].add,tree[(id<<1)|1].Max+tree[(id<<1)|1].add);
tree[id].add=0;
}
if(right<=mid)
return query(id<<1,left,right);
else if(left>mid )
return query((id<<1)|1,left,right);
else
{
return max(query(id<<1,left,mid),query((id<<1)|1,mid+1,right));
}
}
//*****************************************************************
RMQ问题的ST算法
//*****************************************************************
const int MAXN=50000+100;
const int Mpow=16;//保证2^(Mpow)>MAXN即可
int data[MAXN];
int Maxdp[MAXN][Mpow];
int Mindp[MAXN][Mpow];
inline int Min(int a,int b)
{
return a<b?a:b;
}
inline int Max(int a,int b)
{
return a>b?a:b;
}
inline void init(int n)
{
for(int i=1;i<=n;i++)
{
Mindp[i][0]=Maxdp[i][0]=data[i];
}
}
//预处理过程,时间复杂度为O(N*log(N))
//ST算法求区间最值
//dp[i][j]表示区间[i,i+2^j-1]最值
//求dp[i][j]时将其分成dp[i][j-1],dp[i+2^(j-1)][j-1]
//[i,i+2^j-1]=[i,i+2^(j-1)-1]+[i+2^(j-1),i+2^(j-1)+2^(j-1)-1]
inline void Rmp_ST(int n)
{
int l,s;
for(l=1;l<=16;l++)
{
for(s=1;s<=n;s++)
{
if(s+(1<<l)-1<=n)
{
Maxdp[s][l]=Max(Maxdp[s][l-1],Maxdp[s+(1<<(l-1))][l-1]);
Mindp[s][l]=Min(Mindp[s][l-1],Mindp[s+(1<<(l-1))][l-1]);
}
}
}
}
//查询[s,e]区间最值,下标从1-n
//求一个最大的k,是k满足2^k<=e-s+1
//原理:区间[s,e]=区间[s,s+2^k-1]+区间[e-2^k+1,e]
//s<=e-2^k+1,s+2^k-1<=e保证刚好完全覆盖
inline void query()
{
int s,e,Min_ans,Max_ans;
scanf("%d%d",&s,&e);
int k=(int)(log(1.0*e-s+1)/log(2.0));
Min_ans=Min(Mindp[s][k],Mindp[e-(1<<(k))+1][k]);
Max_ans=Max(Maxdp[s][k],Maxdp[e-(1<<(k))+1][k]);
printf("%d\n",Max_ans-Min_ans);
}
//*****************************************************************
一维树状数组
//************************************************************
const int MAXN=8000+100;
int C[MAXN];
int Lowbit[MAXN];
//C[i] = a[i-lowbit(i)+1] + …+ a[i],下表从1开始
//Lowbit[i]=i&(i^(i-1));或Lowbit[i]=i&(-i);
//1.查询
int QuerySum(int p)
//查询原数组中下标1-p的元素的和
{
int nSum=0;
while(p>0)
{
nSum+=C[p];
p-=Lowbit[p];
}
return nSum;
}
//2.修改+初始化
void Modify(int p,int val)
//原数组中下表为p的元素+val,导致C[]数组中部分元素值的改变
{
while(p<=MAXN-10)
{
C[p]+=val;
p+=Lowbit[p];
}
}
//************************************************************
二维树状数组
//*****************************************************************
1.修改
void Add( int y, int x,int a)
//原数组中下表为[y][x]的元素+a,导致C[][]数组中部分元素值的改变
{
while( y <= s )
{
int tmpx = x;
while( tmpx <= s )
{
C[y][tmpx] += a;
tmpx += Lowbit[tmpx];
}
y += Lowbit[y];
}
}
2.查询
int QuerySum( int y, int x)
//查询第1行到第y行,第1列到第x列的和
{
int nSum = 0;
while( y > 0 )
{
int tmpx = x;
while( tmpx > 0)
{
nSum += C[y][tmpx];
tmpx -= Lowbit[tmpx];
}
y -= Lowbit[y];
}
return nSum;
}
//*****************************************************************
划分树
//*****************************************************************
const int MAXN=100010;
int tree[30][MAXN];//表示每层每个位置的值
int sorted[MAXN];//已经排序的数
int toleft[30][MAXN];//toleft[p][i]表示第p层从1到i有多少个数分入左边
//创建划分树
//时间复杂度为O(N*log(N))
void build(int l,int r,int dep)
{
int i;
if(l==r)return;
int mid=(l+r)>>1;
int same=mid-l+1;//表示等于中间值而且被分入左边的个数
for(i=l;i<=r;i++)
if(tree[dep][i]<sorted[mid])
same--;
int lpos=l;
int rpos=mid+1;
for(i=l;i<=r;i++)
{
if(tree[dep][i]<sorted[mid])//比中间的数小,分入左边
tree[dep+1][lpos++]=tree[dep][i];
else if(tree[dep][i]==sorted[mid]&&same>0)
{
tree[dep+1][lpos++]=tree[dep][i];
same--;
}
else //比中间值大分入右边
tree[dep+1][rpos++]=tree[dep][i];
toleft[dep][i]=toleft[dep][l-1]+lpos-l;//从1到i放左边的个数
}
build(l,mid,dep+1);
build(mid+1,r,dep+1);
}
//查询区间第k小的数,[L,R]是大区间,[l,r]是要查询的小区间
//时间复杂度为O(log(N))
int query(int L,int R,int l,int r,int dep,int k)
{
if(l==r)return tree[dep][l];
int mid=(L+R)>>1;
int cnt=toleft[dep][r]-toleft[dep][l-1];//[l,r]中位于左边的个数
if(cnt>=k)
{
//L+要查询的区间前被放在左边的个数
int newl=L+toleft[dep][l-1]-toleft[dep][L-1];
//左端点加上查询区间会被放在左边的个数
int newr=newl+cnt-1;
return query(L,mid,newl,newr,dep+1,k);
}
else
{
int newr=r+toleft[dep][R]-toleft[dep][r];
int newl=newr-(r-l-cnt);
return query(mid+1,R,newl,newr,dep+1,k-cnt);
}
}
Init()
{
memset(toleft,0,sizeof(toleft));
for(i=1;i<=n;i++)
{
scanf("%d",&tree[0][i]);
sorted[i]=tree[0][i];
}
sort(sorted+1,sorted+n+1);
build(1,n,0);
}
//*****************************************************************
字符串匹配
Manacher算法求最长回文子串
//*****************************************************************
const int MAXN=1000000+100;
char str1[MAXN*2],str2[MAXN*2];//待处理字符串
int num[MAXN*2];
//将str1变成str2,如abab变成$#a#b#a#b#
void init()
{
int i,id;
str2[0]='$';
str2[1]='#';
for(i=0,id=2;str1[i];i++,id+=2)
{
str2[id]=str1[i];
str2[id+1]='#';
}
str2[id]=0;
}
//Manacher算法求最长回文子串,时间复杂度为O(N)
int Manacher()
{
int i,ans=0,MxR=0,pos=1;
for(i=1;str2[i];i++)
{
if(MxR>i)num[i]=num[pos*2-i]<(MxR-i)?num[pos*2-i]:(MxR-i);
else num[i]=1;
while(str2[i+num[i]]==str2[i-num[i]])
num[i]++;
if(num[i]+i>MxR)
{
MxR=num[i]+i;
pos=i;
}
if(ans<num[i])
ans=num[i];
}
return ans-1;
}
//*****************************************************************
BF
//*****************************************************************
//BF算法查找str2是否是str1的子串,返回str2在str1中首元素的下标
int BF()
{
int i,j,len1,len2;
len1=strlen(str1);
len2=strlen(str2);
i=0;j=0;
while(i<len1&&j<len2)
{
if(str1[i]==str2[j])
{
i++;
j++;
}
else
{
i=i-j+1;
j=0;
}
}
if(j==len2)
return i-j;
else
return -1;
}
//*****************************************************************
KMP
len % (len - next[len]) == 0,那么循环节的循环次数为len / (len - next[len]),否则为1
//*****************************************************************
char W[MAXN],T[MAXN];//W为模式串,T为主串
int next[MAXN];
//整个KMP算法时间复杂度为O(N+M)
//KMP算法中计算next[]数组
void getNext(char *p)
{
int j,k,len=strlen(p);
j=0;
k=-1;
next[0]=-1;
while(j<len)
{
if(k==-1||p[j]==p[k])
{
next[++j]=++k;
}
else k=next[k];
}
}
//KMP算法统计模式串W在主串T中出现的次数
int KMP_count(char *W,char *T)
{
int i,wlen=strlen(W),tlen=strlen(T),j=0,ans=0;
getNext(W);
for(i=0;i<tlen;i++)
{
while(j>0&&T[i]!=W[j])
j=next[j];
if(W[j]==T[i])j++;
if(j==wlen)
{
ans++;
j=next[j];
}
}
return ans;
}
//返回模式串T在主串S中首次出现的位置
//返回的位置是从0开始的,若没有找到返回-1。
int KMP_Index(char *W,char *T)
{
int i=0, j=0,wlen=strlen(W),tlen=strlen(T);
getNext(W);
while(i<tlen&&j<wlen)
{
if(j==-1||T[i]==W[j])
{
i++; j++;
}
else
j=next[j];
}
if(j==wlen)
return i-wlen;
else
return -1;
}
//*****************************************************************
字符串的最小表示法
//*****************************************************************
//返回母串的最小子串的起始位置,返回值从1开始到strlen(str)
int minpresent(char *str)
{
int i,j,k,len=strlen(str);
i=0,j=1,k=0;
while(i<len&&j<len&&k<len)
{
if(str[(i+k)%len]==str[(j+k)%len])
k++;
else
{
if(str[(i+k)%len]>str[(j+k)%len])
i=i+k+1;
else
j=j+k+1;
if(i==j)j++;
k=0;
}
}
return ++i<++j?i:j;
}
//*****************************************************************
字典树
//************************************************************
//定义字典树结构体
struct Trie
{
Trie* next[26];
int flag;
};
Trie *root;
//初始化root
void init()
{
root=new Trie;
memset(root,0,sizeof(Trie));
}
//2.插入
//将str插入以root为根节点的字典树中
void insert(char *str)
{
int len = strlen(str);
Trie *s = root;
for (int i = 0; i < len; i++)
{
if (s->next[str[i] - 'a'])
s = s->next[str[i] - 'a'];
else
{
Trie* t = new Trie;
memset(t, 0, sizeof (Trie));
s->next[str[i] - 'a'] = t;
s = t;
}
}
s->flag = 1;
}
//3.查找
//查找字典树中是否有元素str
int find(char *str)
{
int len = strlen(str);
Trie *s = root;
for (int i = 0; i < len; i++)
{
if (s->next[str[i] - 'a'])
s = s->next[str[i] - 'a'];
else
return 0;
}
return s->flag;/////////////////////flag可能不标志为单词结尾
}
//5.释放内存空间
//释放以root为根节点的字典树内存空间
void del(Trie *root)
{
Trie *s = root;
for (int i = 0; i < 26; i++)
{
if (s->next[i])
del(s->next[i]);
}
delete s;
s = NULL;
}
//*****************************************************************
AC自动机模板
//************************************************************
const int kind = 26;//视具体情况改动
struct node
{
node *fail; //失败指针
node *next[kind]; //Tire每个节点的26个子节点(最多26个字母)
int count; //是否为该单词的最后一个节点
node()
{ //构造函数初始化
fail=NULL;
count=0;
memset(next,NULL,sizeof(next));
}
}*q[1000*255]; //队列方便用于bfs构造失败指针,大小应依据Tries图//节点个数而定
int head,tail; //队列的头尾指针
node *Root;
//1.建立Tries
void insert(char *str,node *root)
//建立一颗以root为根节点的不带前缀指针的字典树
{
node *p=root;
int i=0,index;
while(str[i])
{
index=str[i]-'A';//视具体情况改动
if(p->next[index]==NULL)
p->next[index]=new node();
p=p->next[index];
i++;
}
p->count=1;
}
//2.建立前缀指针,形成Tries图
void build_ac_automation(node *root)
//在建好的字典树上添加前缀指针,形成Tries图,即ac自动机
{
int i;
root->fail=NULL;
q[head++]=root;
while(head!=tail)
{
node *temp=q[tail++];
node *p=NULL;
for(i=0;i<kind;i++)
{
if(temp->next[i]!=NULL)
{
if(temp==root)
temp->next[i]->fail=root;
else
{
p=temp->fail;
while(p!=NULL)
{
if(p->next[i]!=NULL)
{
temp->next[i]->fail=p->next[i];
break;
}
p=p->fail;
}
if(p==NULL)
temp->next[i]->fail=root;
}
q[head++]=temp->next[i];
}
}
}
}
//3.查询母串
int query(node *root,char *s)
//有多少种模式串出现在母串str[]中
{
int i=0,cnt=0,index,len=strlen(s);
node *p=root;
while(s[i])
{
index=s[i]-'A';//视具体情况改动
while(p->next[index]==NULL && p!=root)
p=p->fail;
p=p->next[index];
p=(p==NULL)?root:p;
node *temp=p;
while(temp!=root&&temp->count)
{
cnt+=temp->count;
temp->count=0;
temp=temp->fail;
}
i++;
}
return cnt;
}
//4.初始化
void init()
{
head=tail=0;
Root=new node;
}
//************************************************************
后缀数组
//*****************************************************************
const int MAXN=100000+100;
char str[MAXN];//待处理字符串
int sa[MAXN];//求得的后缀数组
int wa[MAXN],wb[MAXN],wv[MAXN],wh[MAXN];
int cmp(int *r,int a,int b,int l)
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}
//求后缀数组sa[],下标1到n-1(此处n=strlen(str)+1)有效后缀
//将str的n个后缀从小到大进行排序之后把排好序的后缀的开头位置顺次放入sa中。
//保证Suffix(sa[i])<Suffix(sa[i+1])
//1<=i<n,sa[0]存放人为添加在末尾的那个最小的后缀
//倍增算法的时间复杂度为O(nlogn)
//倍增算法的空间复杂度都是O(n)
void da(char *r,int *sa,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=0;i<m;i++) wh[i]=0;
for(i=0;i<n;i++) wh[x[i]=r[i]]++;
for(i=1;i<m;i++) wh[i]+=wh[i-1];
for(i=n-1;i>=0;i--) sa[--wh[x[i]]]=i;
for(j=1,p=1;p<n;j*=2,m=p)
{
for(p=0,i=n-j;i<n;i++) y[p++]=i;
for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
for(i=0;i<n;i++) wv[i]=x[y[i]];
for(i=0;i<m;i++) wh[i]=0;
for(i=0;i<n;i++) wh[wv[i]]++;
for(i=1;i<m;i++) wh[i]+=wh[i-1];
for(i=n-1;i>=0;i--) sa[--wh[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
return;
}
int rank[MAXN],height[MAXN];
//定义height[i]=suffix(sa[i-1])和suffix(sa[i])的最长公
//共前缀,也就是排名相邻的两个后缀的最长公共前缀
//任意两个起始位置为i,j(假设rank[i]<rank[j])的后缀的最长公共前缀
//为height[rank[i]+1]、height[rank[i]+2]…height[rank[j]]的最小值
void calheight(char *r,int *sa,int n)
{
int i,j,k=0;
for(i=1;i<=n;i++) rank[sa[i]]=i;
for(i=0;i<n;height[rank[i++]]=k)
for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
return;
}
//求不可重叠最长重复子串
//先二分答案,把题目变成判定性问题:判断是否
//存在两个长度为k 的子串是相同的,且不重叠
//时间复杂度为O(N*log(N))
int Bin_Solve(int n)
{
int i,Minlen,Maxlen,midlen,lowid,highid;
bool flag;
Minlen=0,Maxlen=n/2;
while(Minlen<=Maxlen)//二分重复字串的长度
{
midlen=(Minlen+Maxlen)/2;
lowid=n+1,highid=0;
flag=false;
for(i=1;i<=n&&!flag;i++)//此处i表示排名
{
if (height[i]<midlen)lowid=highid=sa[i];//中间有一个的长度不大于midlen,就断开了
//即说明前面的和后面的最长公共前缀不可能大于等于miflen
else if(height[i]>=midlen)//长度大于等于midlen
{
lowid=min(lowid,sa[i]);
highid=max(highid,sa[i]);
if(highid-lowid>=midlen)//且不重叠
flag=true;
}
}
if(flag)Minlen=midlen+1;
else Maxlen=midlen-1;
}
return Maxlen<4?0:Maxlen+1;
}
//*****************************************************************
4.图论
邻接表建图
//*****************************************************
const int MAXN=1000+100;
const int INF = 0x3f3f3f3f;
int head[MAXN];
int dis[MAXN];
bool visited[MAXN];
int qq[MAXN];//模拟队列
struct node
{
int to;
int next;
}Edg[20000+100];
int n,m,tot;
void init()
{
tot=0;
memset(head,-1,sizeof(head));
}
void add(int a,int b)
{
Edg[tot].to=b;
Edg[tot].next=head[a];
head[a]=tot++;
}
void BFS(int s)
{
//queue<int>qq;
//qq.push(s);
int front,rear;
front=rear=0;
qq[front++]=s;
int now,i,to;
for(i=1;i<=n;i++)
{
if(i!=s)dis[i]=INF;
else dis[i]=0;
}
visited[s]=true;
while(rear<front)
{
//now=qq.front();
//qq.pop();
now=qq[rear++];
for(i=head[now];i!=-1;i=Edg[i].next)
{
to=Edg[i].to;
if(to==now||visited[to])continue;
dis[to]=dis[now]+1;
//qq.push(to);
qq[front++]=to;
visited[to]=true;
}
}
}
//*****************************************************
二分图:
模板一:匈牙利算法
//************************************************************
//二分图匹配(匈牙利算法的DFS实现)
//初始化:g[][]两边顶点的划分情况
//建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配
//g没有边相连则初始化为0
//uN是匹配左边的顶点数,vN是匹配右边的顶点数
//调用:res=hungary();输出最大匹配数
//优点:适用于稠密图,DFS找增广路,实现简洁易于理解
//时间复杂度:O(VE)
//************************************************************
//顶点编号从0开始的
const int MAXN=510;
int uN,vN;//u,v数目
int g[MAXN][MAXN];
int linker[MAXN];
bool visited[MAXN];
bool dfs(int u)//从左边开始找增广路径
{
int v;
for(v=0;v<vN;v++)//这个顶点编号从0开始,若要从1开始需要修改
if(g[u][v]&&!visited[v])
{
visited[v]=true;
if(linker[v]==-1||dfs(linker[v]))
{//找增广路,反向
linker[v]=u;
return true;
}
}
return false;//这个不要忘了,经常忘记这句
}
int hungary()
{
int res=0;
int u;
memset(linker,-1,sizeof(linker));
for(u=0;u<uN;u++)
{
memset(visited,0,sizeof(visited));
if(dfs(u)) res++;
}
return res;
}
//****************************************************
最小生成树的Kruskal算法
//************************************************************
//带权值的无向图的最小生成树的Kruskal算法
int id[maxm];//id[]存放边的下标,从[0-m-1]
int eu[maxm],ev[maxm],ew[maxm];//依次存放无向边的两顶点和权值
int n,m;//n为顶点数,m为边数
int par[maxn];//并查集中存放顶点的数组
int cmp( const int &i , const int &j)
{
return ew[i]<ew[j];
}
int Get_Par( int x)
{
if( par[x]==x)
return par[x];
par[x]=Get_Par(par[x]) ;
return par[x] ;
}
int Kruskal( )
{
int ret=0, i , j , p ;
for(i=1;i<=n;i++)
par[i]=i; // node [ 1 . . n ]
for(i=0;i<m;i++)
id[i]=i ; // ew [ 0 . .m.1]
std::sort(id,id+m,cmp) ;
for(j=-1,i=1;i<n;i++)
{
while(p=id[++j],Get_Par(eu[p])==Get_Par(ev[p]));
ret+=ew[p];
par[Get_Par(ev[p])]=Get_Par(eu[p]) ;
}
return ret;
}
//************************************************************
Dijkstra算法
//************************************************************
#define INF 0x3f3f3f3f
#define Max 155
int n;//表示顶点数目
int dis[Max];//dis数组表示源点到各点的距离
int g[Max][Max];//用邻接矩阵g[][]存放图的边
bool visited[Max];//标记原点到该点的最短距离是否找到
//开始应初始化memset(g, INF, sizeof(g));
void Dijkstra(int start)
{
int temp,k,i,j;
memset(visited,false,sizeof(visited));
for(i=1;i<=n;++i)
dis[i]=g[start][i];
dis[start]=0;
visited[start]=1;
for(i=1;i<=n;++i)
{
temp=INF;
for(int j=1;j<=n;++j)
if(!visited[j]&&temp>dis[j])
temp=dis[k=j];
if(temp==INF)break;
visited[k]=1;
for(j=1;j<=n;++j)
if(!visited[j]&&dis[j]>dis[k]+g[k][j])
dis[j]=dis[k]+g[k][j];
}
}
//************************************************************
flyod算法
//*****************************************************************
int const Max=1001;
int a[Max][Max];//存放边的权值
int b[Max];//存放顶点的权值
int nex[Max][Max]; //next[i][j]用来保存i-->j的最短路径中i的最优后即最近的顶点
int N;
void floyd()
//flyod算法求个顶点间的最短路径长度并记录路径
{
int i,j,k,fee;
for(i=1;i<=N;i++)
for(j=1;j<=N;j++)
nex[i][j]=j;
for(k=1;k<=N;k++)
{
for(i=1;i<=N;i++)
{
if(i==k||a[i][k]==-1)
continue;
for(j=1;j<=N;j++)
{
if(a[k][j]==-1||j==k)
continue;
fee = a[i][k]+a[k][j]+b[k];
if(a[i][j]==-1||a[i][j]>fee)
{
a[i][j]=fee;
nex[i][j]=nex[i][k];
}
//选择字典序小的路径
else if(a[i][j]==fee)
{
if(nex[i][j]>nex[i][k])
nex[i][j]=nex[i][k];
}
}
}
}
}
void path(int i, int j)
//递归输出最短路径
{
if(j==nex[i][j])
{
printf("%d-->%d\n",i,j);
}
else
{
printf("%d-->",i);
path(nex[i][j],j);
}
}
//*****************************************************************
数论
矩阵快速幂
//*****************************************************
//origin存放需计算的矩阵,res存放答案矩阵
//最终答案为res.a[1][0](对应f
)
struct matrix
{
__int64 a[2][2];
};
matrix origin,res;
//将res初始化为初始条件矩阵,人为输入关系递推矩阵origin
void init()
{
origin.a[0][0]=1,origin.a[1][0]=origin.a[0][1]=1,origin.a[1][1]=0;
res.a[0][0]=1,res.a[0][1]=res.a[1][0]=res.a[1][1]=0;
}
//直接将2个矩阵相乘x*y,返回计算后的矩阵
matrix multiply(matrix &x,matrix &y,__int64 MOD)
{
matrix temp;
memset(temp.a,0,sizeof(temp.a));
for(int i=0;i<2;i++)
{
for(int j=0;j<2;j++)
{
for(int k=0;k<2;k++)
{
temp.a[i][j]+=x.a[i][k]*y.a[k][j];
temp.a[i][j]%=MOD;
}
}
}
return temp;
}
//矩阵快速幂的计算,矩阵的n次幂,每个中间结果对MOD取模
void calc(matrix &origin,matrix &res,__int64 n,__int64 MOD)
{
while(n)
{
if(n&1)
res=multiply(origin,res,MOD);
n>>=1;
origin=multiply(origin,origin,MOD);
}
}
//*****************************************************
快速幂
A^B %C=A^( B%phi(C)+phi(C) ) %C B>=phi(C)
//************************************************************
//快速幂x^n%mod的计算
__int64 optimized_pow_n(__int64 x, __int64 n)
{
__int64 pw = 1;
while (n > 0)
{
if (n & 1)
pw *= x;
pw=pw%mod;
x *= x;
x=x%mod;
n >>= 1;
}
return pw;
}
//************************************************************
三分法求凹凸函数的极值点
//*****************************************************
//当需要求某凸性或凹形函数的极值,通过函数本身表达式并不容易求解时,就可以用三分法不断逼近求解
double mid, midmid;
while ( low + eps < high )
{
mid = (low + high) / 2;
midmid = (mid + high ) / 2;
double cmid = cal(mid);
double cmidmid = cal(midmid);
if ( cmid > cmidmid )
high = midmid;
else
low = mid;
}
//*****************************************************
普通母函数
//*****************************************************
//普通母函数,求组合数。
int n,sum;//n表示物品种类数,sum为在[1,sum]范围类求每个价值的组合数(不排列)
int num[100+10],value[100+10];//num[]存放该种类对应的个数,value[]存放该种类对应的价值
int a[10000+100];
int b[10000+100];
void Generating_function()
{
int i,j,k;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
a[0]=1;
for(i=1;i<=n;i++)
{
for(j=0;j<=sum;j++)
{
for(k=0;k<=num[i]&&k*value[i]+j<=sum;k++)
{
b[k*value[i]+j]+=a[j];
b[k*value[i]+j]%=10000;
}
}
memcpy(a,b,sizeof(b));
memset(b,0,sizeof(b));
}
}
//*****************************************************
最大公约数
//*****************************************************
//求a,b的最大公约数
LL Gcd(LL a,LL b)
{
return b==0?a:Gcd(b,a%b);
}
//*****************************************************
计算几何
//求不共线三点的外接圆,利用圆心到三点距离相等联立方程求得
point GetCentre(point a,point b,point c)
{
double a1=b.x-a.x,b1=b.y-a.y,c1=(a1*a1+b1*b1)/2;
double a2=c.x-a.x,b2=c.y-a.y,c2=(a2*a2+b2*b2)/2;
double d=a1*b2-a2*b1;
point ret;
ret.x=a.x+(c1*b2-c2*b1)/d;
ret.y=a.y+(a1*c2-a2*c1)/d;
return ret;
}
头文件及宏定义
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include <numeric>
using namespace std;
const double eps(1e-8);
const int INF = 0x3f3f3f3f;
//优先级队列的两种定义方式
struct cmp//对应大顶堆
{
bool operator()(int a,int b)
{
return a<b;
}
};
priority_queue<int,vector<int>,cmp>Q;
struct node
{
int id;
bool operator < (const node& b)const
{
return id>b.id;
}
};
priority_queue<node>Q;
//set,multiset用法
struct cmp
{
bool operator ()(const node &a,const node &b)const
{
return a.nam<b.nam;//从小到大排序
}
};
multiset<node,cmp>MulSet;//存放未处理
处理求矩阵的最大子矩
//************************************************************
//求a
[m]的最大子矩阵
///计算从某固定行开始起连续列b[i,j]动态规划求最大值
int dp( const int *b,int m)
{
int sum ,max,i;
sum=max=b[0];
for(i=1;i<m;i++)
{
if(sum>0)sum+=b[i];
else sum=b[i];
if(sum>max)max=sum;
}
return max;
}
//b[k]可以取到任意连续行的排列组合
max=-999999999;
for(i=0;i<n;i++)//控制具体哪一行开始
{
memset(b,0,sizeof(b));
for(j=i;j<n;j++)//表示从第i行到最后n行中间每列累加得b[k]
{
for(k=0;k<m;k++)
b[k]+=a[j][k];
if(dp(b,m)>max) max=dp(b,m);
}
}
//************************************************************
动态规划求最大面积
//************************************************************
{
首先开辟数组a
,l
,r
////找出其左右区间比其大的区间长度
l[1]=1;
r
=n;
for (i=2; i<=n; ++i)
{
t=i;
while (t>1 && a[i]<=a[t-1]) t=l[t-1];
l[i]=t;
}
for (i=n-1; i>=1; --i)
{
t=i;
while (t<n && a[i]<=a[t+1]) t=r[t+1];
r[i]=t;
}
/////分别以每个小矩形的高度为高求大矩形面积,并找出其最大者
max=0;
for (i=1; i<=n; ++i)
{
if ((r[i]-l[i]+1)*a[i]>max) max=(r[i]-l[i]+1)*a[i];
}
}
//************************************************************
最长公共子序列
//*********************************************************
//算法1:时间复杂度为O(N*M),空间复杂度为O(N*N)
const int MAXN=1000;
char str1[MAXN],str2[MAXN];
int dp[MAXN][MAXN];
int LCS1(char *str1,char *str2)
{
int len1=strlen(str1);
int len2=strlen(str2);
int i,j;
memset(dp,0,sizeof(dp));
for(i=1;i<=len1;i++)
{
for(j=1;j<=len2;j++)
{
if(str1[i-1]==str2[j-1])
dp[i][j]=dp[i-1][j-1]+1;
else if(dp[i-1][j]>dp[i][j-1])
dp[i][j]=dp[i-1][j];
else dp[i][j]=dp[i][j-1];
}
}
return dp[len1][len2];
}
//===============================================//算法2:时间复杂度为O(N*M),空间复杂度为O(N)
const int MAXN=1000;
char str1[MAXN],str2[MAXN];
int dp[2][MAXN];//采用滚动数组优化
int LCS2(char *str1,char *str2)
{
int len1=strlen(str1);
int len2=strlen(str2);
int i,j,k;
memset(dp,0,sizeof(dp));
for(i=1;i<=len1;i++)
{
k=i&1;
for(j=1;j<=len2;j++)
{
if(str1[i-1]==str2[j-1])
dp[k][j]=dp[k][j-1]+1;
else if(dp[k^1][j]>dp[k][j-1])
dp[k][j]=dp[k^1][j];
else dp[k][j]=dp[k][j-1];
}
}
return dp[k][len2];
}
//*********************************************************
最长公共递增子序列
//************************************************************
//其中a
,b
分别存放俩序列
memset(dp,0,sizeof(dp));
max=0;
for(i=0;i<n;i++)
{
k=0;
for(j=0;j<m;j++)
{
//////////////保证递增又保证找到最大
if(a[i]>b[j]&&dp[k]<dp[j])
k=j;
if(a[i]==b[j])
dp[j]=dp[k]+1;
if(max<dp[j])
max=dp[j];
}
}
//************************************************************
数塔问题
//************************************************************
//n,m分别表示行数列数
void shuta(int n,int m)
{
int i,j;
memset(data,0,sizeof(data));
memset(dp,0,sizeof(dp));
for(i=1;i<=n;i++)
{
for(j=1;j<=i;j++)
{
cin>>data[i][j];
}
}
for(j=1;j<=m;j++)
dp
[j]=data
[j];
for(i=n-1;i>=1;i--)
{
for(j=i;j>=1;j--)
{
dp[i][j]=max(dp[i+1][j]+data[i][j],dp[i+1][j+1]+data[i][j]);
}
}
}
//************************************************************
最长递增子序列
//求严格递增子序列的最长长度
//************************************************************
//算法1的时间复杂度为O(N*log(N))
const int MAXN=1000;
int data[MAXN];//存放原始数据
int MaxV[MAXN];//MaxV[i]存放长度为i的严格递增子序列的最大值的最小值
//二分查找返回MaxV中大于等于x的组靠左的下标
int BinaryResearch(int x,int len)
{
int mid,low=1,high=len;
while(low<=high)
{
mid=(low+high)>>1;
if(MaxV[mid]<x)
low=mid+1;
else high=mid-1;
}
return low;
}
//返回原序列中严格递增子序列的最长长度
int LIS2(int n)
{
int i,len=1;
MaxV[1]=data[0];
for(i=1;i<n;i++)
{
if(data[i]>MaxV[len])//比长度为len的子序列最大值大,直接加进末尾
MaxV[++len]=data[i];
else
{
int pos=BinaryResearch(data[i],len);
MaxV[pos]=data[i];
}
}
return len;
}
//===============================================
//算法2的时间复杂度为O(N*N)
int dp[MAXN];
//返回原序列中严格递增子序列的最长长度
int LIS1(int n)
{
int i,j,lmax;
lmax=0;
for(i=0;i<n;i++)
{
dp[i]=1;
for(j=0;j<i;j++)
{
if(data[i]>data[j]&&dp[j]+1>dp[i])
dp[i]=dp[j]+1;
}
if(dp[i]>lmax)lmax=dp[i];
}
return lmax;
}
//************************************************************
最大字段和
//************************************************************
void MSS(int n)
{
int i;
int max=NINF;
memset(dp,0,sizeof(dp));
for(i=1;i<=n;i++)
{
if(dp[i-1]<=0)
dp[i]=data[i];
else dp[i]=dp[i-1]+data[i];
if(max<dp[i])
max=dp[i];
}
cout<<max<<endl;
}
//************************************************************
多重背包
//************************************************************
int dp[100000+10];//视具体情况而定
struct node
{
int num,weight,value;//分别代表每种物品的数量、重量、价值
}Good[15];//定义每个物品的结构体
int Max_weight;//背包的载重量
int max(int a,int b)
<
ad8cd
li style="list-style-position:outside;border-style:none none none solid;border-left-width:3px;border-left-color:rgb(108,226,108);line-height:18px;background-color:rgb(248,248,248);margin-right:0px;margin-bottom:0px;padding:0px 3px 0px 10px;">{
return a<b?b:a;
}
void ZeroOnePack(int weight,int value,int lim)
//对应01背包的求法
{
for(int i=lim;i>=weight;i--)
dp[i]=max(dp[i],dp[i-weight]+value);
}
void CompletePack(int weight,int value,int lim)
//对应完全背包的求法
{
for(int i=weight;i<=lim;i++)
dp[i]=max(dp[i],dp[i-weight]+value);
}
void MultiplePack(int weight,int value,int amount,int lim)
//选定物品后的多重背包的求法
{
if(weight*amount>=lim)CompletePack(weight,value,lim);
else
{
for(int k=1;k<amount;)
{
ZeroOnePack(k*weight,k*value,lim);
amount-=k;
k<<=1;
}
ZeroOnePack(amount*weight,amount*value,lim);
}
}
void Solve(int n)
//解决物品种类数为n的多重背包问题求法
{
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
MultiplePack(Good[i].weight,Good[i].value,Good[i].num,Max_weight);
}
//************************************************************
数据结构
单调队列
const int MAXN=100000+10;
int da[MAXN],Inc[MAXN],Dec[MAXN];
int n,m,k,front1,rear1,front2,rear2;
//维护双端单调队列,求da[]中满足m<=Max-Min<=k最长连续子序列
int Queue()
{
front1=0,rear1=-1,front2=0,rear2=-1;
int i,ans=0,start=0;
for(i=0;i<n;i++)
{
while(front1<=rear1&&da[Dec[rear1]]<=da[i])//保证Dec队列在start-i区间内的递减
rear1--;
Dec[++rear1]=i;
while(front2<=rear2&&da[Inc[rear2]]>=da[i])//保证inc队列在start-i区间内的递增
rear2--;
Inc[++rear2]=i;
while(da[Dec[front1]]-da[Inc[front2]]>k)
{
//保证区间左端点向后滑动一个长度
if(Dec[front1]-Inc[front2]<0)
{
start=Dec[front1]+1;
front1++;
}
else
{
start=Inc[front2]+1;
front2++;
}
}
//满足m<=Max-Min<=k
if(da[Dec[front1]]-da[Inc[front2]]>=m)
{
if(i-start+1>ans)
ans=i-start+1;
}
}
return ans;
}
并查集
//*****************************************************************
1.求父亲节点并压缩路径
int par[MAX];
int Get_par(int a)
//查找a的父亲节点并压缩路径
{
if(par[a]==a)
return par[a];
//注意语句的顺序
int pa=par[a];
par[a]=Get_par(par[a]);
//
return par[a];
}
2.合并
void Merge(int a,int b)
//合并a,b
{
int pa,pb;
pa=Get_par(a);
pb=Get_par(b);
par[pa]=pb;
}
//*****************************************************************
线段树
不带延迟更新的操作
//************************************************************
int da[MAXN];
struct node
{
int left,right,sum;//sum此处灵活处理
}tree[MAXN*4];
//1.建立以left,right为左右边界,将数组da中元素存储在首地址从1开始的线段树tree的叶节点上
void Build( int id,int left,int right)
{
tree[id].left=left;
tree[id].right=right;
if(left==right)
{
//tree[id].sum=da[left];//此处可以直接初始化为对应da[left]
return ;
}
else
{
int mid =(left+right)>>1;
Build(id<<1,left,mid);
Build((id<<1)|1,mid+1,right);
//tree[id].sum=tree[(id<<1)].sum+tree[(id<<1)|1].sum;
}
}
//2.在线段树的叶节点pos处加val
void Updata(int id,int pos,int val)
{
tree[id].sum+=val;
if(tree[id].left==tree[id].right&&tree[id].left==pos)
{
return ;
}
int mid=(tree[id].left+tree[id].right)>>1;
if(pos<=mid)
Updata(id<<1,pos,val);
else
Updata((id<<1)|1,pos,val);
}
//3.查询区间[left,right]上的和
int Query(int id,int left,int right)
{
if(tree[id].left==left&&tree[id].right==right)
{
return tree[id].sum;
}
int mid=(tree[id].left+tree[id].right)>>1;
if(right<=mid)
return Query(id<<1,left,right);
if(left>=mid+1)
return Query((id<<1)|1,left,right);
return Query(id<<1,left,mid)+Query((id<<1)|1,mid+1,right);
}
//************************************************************
线段树的延迟更新
//*****************************************************
const int MAXN=100000+100;
struct node
{
int left,right,add;
int Max;
}tree[MAXN*4];
void build(int id, int left, int right)
{
tree[id].add=0;
tree[id].left=left;
tree[id].right=right;
tree[id].Max=0;
if(left==right)
{
return ;
}
else
{
int mid=(left+right)>>1;
build(id<<1,left,mid);
build((id<<1)|1,mid+1,right);
}
}
void updata(int id,int left,int right,int adi)
{
if(tree[id].left==left&&tree[id].right==right)
{
tree[id].add+=adi;
return ;
}
int mid=(tree[id].left+tree[id].right)>>1;
if(right<=mid)
updata(id<<1,left,right,adi);
else if(left>mid)
updata((id<<1)|1,left,right,adi);
else
{
updata(id<<1,left,mid,adi);
updata((id<<1)|1,mid+1,right,adi);
}
tree[id].Max=max(tree[id<<1].Max+tree[id<<1].add,tree[(id<<1)|1].Max+tree[(id<<1)|1].add);
}
int query(int id,int left,int right)
{
if(tree[id].left==left&&tree[id].right==right)
{
return tree[id].Max+tree[id].add;
}
int mid=(tree[id].left+tree[id].right)>>1;
if(tree[id].add!=0)
{
updata(id<<1,tree[id].left,mid,tree[id].add);
updata((id<<1)|1,mid+1,tree[id].right,tree[id].add);
tree[id].Max=max(tree[id<<1].Max+tree[id<<1].add,tree[(id<<1)|1].Max+tree[(id<<1)|1].add);
tree[id].add=0;
}
if(right<=mid)
return query(id<<1,left,right);
else if(left>mid )
return query((id<<1)|1,left,right);
else
{
return max(query(id<<1,left,mid),query((id<<1)|1,mid+1,right));
}
}
//*****************************************************************
RMQ问题的ST算法
//*****************************************************************
const int MAXN=50000+100;
const int Mpow=16;//保证2^(Mpow)>MAXN即可
int data[MAXN];
int Maxdp[MAXN][Mpow];
int Mindp[MAXN][Mpow];
inline int Min(int a,int b)
{
return a<b?a:b;
}
inline int Max(int a,int b)
{
return a>b?a:b;
}
inline void init(int n)
{
for(int i=1;i<=n;i++)
{
Mindp[i][0]=Maxdp[i][0]=data[i];
}
}
//预处理过程,时间复杂度为O(N*log(N))
//ST算法求区间最值
//dp[i][j]表示区间[i,i+2^j-1]最值
//求dp[i][j]时将其分成dp[i][j-1],dp[i+2^(j-1)][j-1]
//[i,i+2^j-1]=[i,i+2^(j-1)-1]+[i+2^(j-1),i+2^(j-1)+2^(j-1)-1]
inline void Rmp_ST(int n)
{
int l,s;
for(l=1;l<=16;l++)
{
for(s=1;s<=n;s++)
{
if(s+(1<<l)-1<=n)
{
Maxdp[s][l]=Max(Maxdp[s][l-1],Maxdp[s+(1<<(l-1))][l-1]);
Mindp[s][l]=Min(Mindp[s][l-1],Mindp[s+(1<<(l-1))][l-1]);
}
}
}
}
//查询[s,e]区间最值,下标从1-n
//求一个最大的k,是k满足2^k<=e-s+1
//原理:区间[s,e]=区间[s,s+2^k-1]+区间[e-2^k+1,e]
//s<=e-2^k+1,s+2^k-1<=e保证刚好完全覆盖
inline void query()
{
int s,e,Min_ans,Max_ans;
scanf("%d%d",&s,&e);
int k=(int)(log(1.0*e-s+1)/log(2.0));
Min_ans=Min(Mindp[s][k],Mindp[e-(1<<(k))+1][k]);
Max_ans=Max(Maxdp[s][k],Maxdp[e-(1<<(k))+1][k]);
printf("%d\n",Max_ans-Min_ans);
}
//*****************************************************************
一维树状数组
//************************************************************
const int MAXN=8000+100;
int C[MAXN];
int Lowbit[MAXN];
//C[i] = a[i-lowbit(i)+1] + …+ a[i],下表从1开始
//Lowbit[i]=i&(i^(i-1));或Lowbit[i]=i&(-i);
//1.查询
int QuerySum(int p)
//查询原数组中下标1-p的元素的和
{
int nSum=0;
while(p>0)
{
nSum+=C[p];
p-=Lowbit[p];
}
return nSum;
}
//2.修改+初始化
void Modify(int p,int val)
//原数组中下表为p的元素+val,导致C[]数组中部分元素值的改变
{
while(p<=MAXN-10)
{
C[p]+=val;
p+=Lowbit[p];
}
}
//************************************************************
二维树状数组
//*****************************************************************
1.修改
void Add( int y, int x,int a)
//原数组中下表为[y][x]的元素+a,导致C[][]数组中部分元素值的改变
{
while( y <= s )
{
int tmpx = x;
while( tmpx <= s )
{
C[y][tmpx] += a;
tmpx += Lowbit[tmpx];
}
y += Lowbit[y];
}
}
2.查询
int QuerySum( int y, int x)
//查询第1行到第y行,第1列到第x列的和
{
int nSum = 0;
while( y > 0 )
{
int tmpx = x;
while( tmpx > 0)
{
nSum += C[y][tmpx];
tmpx -= Lowbit[tmpx];
}
y -= Lowbit[y];
}
return nSum;
}
//*****************************************************************
划分树
//*****************************************************************
const int MAXN=100010;
int tree[30][MAXN];//表示每层每个位置的值
int sorted[MAXN];//已经排序的数
int toleft[30][MAXN];//toleft[p][i]表示第p层从1到i有多少个数分入左边
//创建划分树
//时间复杂度为O(N*log(N))
void build(int l,int r,int dep)
{
int i;
if(l==r)return;
int mid=(l+r)>>1;
int same=mid-l+1;//表示等于中间值而且被分入左边的个数
for(i=l;i<=r;i++)
if(tree[dep][i]<sorted[mid])
same--;
int lpos=l;
int rpos=mid+1;
for(i=l;i<=r;i++)
{
if(tree[dep][i]<sorted[mid])//比中间的数小,分入左边
tree[dep+1][lpos++]=tree[dep][i];
else if(tree[dep][i]==sorted[mid]&&same>0)
{
tree[dep+1][lpos++]=tree[dep][i];
same--;
}
else //比中间值大分入右边
tree[dep+1][rpos++]=tree[dep][i];
toleft[dep][i]=toleft[dep][l-1]+lpos-l;//从1到i放左边的个数
}
build(l,mid,dep+1);
build(mid+1,r,dep+1);
}
//查询区间第k小的数,[L,R]是大区间,[l,r]是要查询的小区间
//时间复杂度为O(log(N))
int query(int L,int R,int l,int r,int dep,int k)
{
if(l==r)return tree[dep][l];
int mid=(L+R)>>1;
int cnt=toleft[dep][r]-toleft[dep][l-1];//[l,r]中位于左边的个数
if(cnt>=k)
{
//L+要查询的区间前被放在左边的个数
int newl=L+toleft[dep][l-1]-toleft[dep][L-1];
//左端点加上查询区间会被放在左边的个数
int newr=newl+cnt-1;
return query(L,mid,newl,newr,dep+1,k);
}
else
{
int newr=r+toleft[dep][R]-toleft[dep][r];
int newl=newr-(r-l-cnt);
return query(mid+1,R,newl,newr,dep+1,k-cnt);
}
}
Init()
{
memset(toleft,0,sizeof(toleft));
for(i=1;i<=n;i++)
{
scanf("%d",&tree[0][i]);
sorted[i]=tree[0][i];
}
sort(sorted+1,sorted+n+1);
build(1,n,0);
}
//*****************************************************************
字符串匹配
Manacher算法求最长回文子串
//*****************************************************************
const int MAXN=1000000+100;
char str1[MAXN*2],str2[MAXN*2];//待处理字符串
int num[MAXN*2];
//将str1变成str2,如abab变成$#a#b#a#b#
void init()
{
int i,id;
str2[0]='$';
str2[1]='#';
for(i=0,id=2;str1[i];i++,id+=2)
{
str2[id]=str1[i];
str2[id+1]='#';
}
str2[id]=0;
}
//Manacher算法求最长回文子串,时间复杂度为O(N)
int Manacher()
{
int i,ans=0,MxR=0,pos=1;
for(i=1;str2[i];i++)
{
if(MxR>i)num[i]=num[pos*2-i]<(MxR-i)?num[pos*2-i]:(MxR-i);
else num[i]=1;
while(str2[i+num[i]]==str2[i-num[i]])
num[i]++;
if(num[i]+i>MxR)
{
MxR=num[i]+i;
pos=i;
}
if(ans<num[i])
ans=num[i];
}
return ans-1;
}
//*****************************************************************
BF
//*****************************************************************
//BF算法查找str2是否是str1的子串,返回str2在str1中首元素的下标
int BF()
{
int i,j,len1,len2;
len1=strlen(str1);
len2=strlen(str2);
i=0;j=0;
while(i<len1&&j<len2)
{
if(str1[i]==str2[j])
{
i++;
j++;
}
else
{
i=i-j+1;
j=0;
}
}
if(j==len2)
return i-j;
else
return -1;
}
//*****************************************************************
KMP
len % (len - next[len]) == 0,那么循环节的循环次数为len / (len - next[len]),否则为1
//*****************************************************************
char W[MAXN],T[MAXN];//W为模式串,T为主串
int next[MAXN];
//整个KMP算法时间复杂度为O(N+M)
//KMP算法中计算next[]数组
void getNext(char *p)
{
int j,k,len=strlen(p);
j=0;
k=-1;
next[0]=-1;
while(j<len)
{
if(k==-1||p[j]==p[k])
{
next[++j]=++k;
}
else k=next[k];
}
}
//KMP算法统计模式串W在主串T中出现的次数
int KMP_count(char *W,char *T)
{
int i,wlen=strlen(W),tlen=strlen(T),j=0,ans=0;
getNext(W);
for(i=0;i<tlen;i++)
{
while(j>0&&T[i]!=W[j])
j=next[j];
if(W[j]==T[i])j++;
if(j==wlen)
{
ans++;
j=next[j];
}
}
return ans;
}
//返回模式串T在主串S中首次出现的位置
//返回的位置是从0开始的,若没有找到返回-1。
int KMP_Index(char *W,char *T)
{
int i=0, j=0,wlen=strlen(W),tlen=strlen(T);
getNext(W);
while(i<tlen&&j<wlen)
{
if(j==-1||T[i]==W[j])
{
i++; j++;
}
else
j=next[j];
}
if(j==wlen)
return i-wlen;
else
return -1;
}
//*****************************************************************
字符串的最小表示法
//*****************************************************************
//返回母串的最小子串的起始位置,返回值从1开始到strlen(str)
int minpresent(char *str)
{
int i,j,k,len=strlen(str);
i=0,j=1,k=0;
while(i<len&&j<len&&k<len)
{
if(str[(i+k)%len]==str[(j+k)%len])
k++;
else
{
if(str[(i+k)%len]>str[(j+k)%len])
i=i+k+1;
else
j=j+k+1;
if(i==j)j++;
k=0;
}
}
return ++i<++j?i:j;
}
//*****************************************************************
字典树
//************************************************************
//定义字典树结构体
struct Trie
{
Trie* next[26];
int flag;
};
Trie *root;
//初始化root
void init()
{
root=new Trie;
memset(root,0,sizeof(Trie));
}
//2.插入
//将str插入以root为根节点的字典树中
void insert(char *str)
{
int len = strlen(str);
Trie *s = root;
for (int i = 0; i < len; i++)
{
if (s->next[str[i] - 'a'])
s = s->next[str[i] - 'a'];
else
{
Trie* t = new Trie;
memset(t, 0, sizeof (Trie));
s->next[str[i] - 'a'] = t;
s = t;
}
}
s->flag = 1;
}
//3.查找
//查找字典树中是否有元素str
int find(char *str)
{
int len = strlen(str);
Trie *s = root;
for (int i = 0; i < len; i++)
{
if (s->next[str[i] - 'a'])
s = s->next[str[i] - 'a'];
else
return 0;
}
return s->flag;/////////////////////flag可能不标志为单词结尾
}
//5.释放内存空间
//释放以root为根节点的字典树内存空间
void del(Trie *root)
{
Trie *s = root;
for (int i = 0; i < 26; i++)
{
if (s->next[i])
del(s->next[i]);
}
delete s;
s = NULL;
}
//*****************************************************************
AC自动机模板
//************************************************************
const int kind = 26;//视具体情况改动
struct node
{
node *fail; //失败指针
node *next[kind]; //Tire每个节点的26个子节点(最多26个字母)
int count; //是否为该单词的最后一个节点
node()
{ //构造函数初始化
fail=NULL;
count=0;
memset(next,NULL,sizeof(next));
}
}*q[1000*255]; //队列方便用于bfs构造失败指针,大小应依据Tries图//节点个数而定
int head,tail; //队列的头尾指针
node *Root;
//1.建立Tries
void insert(char *str,node *root)
//建立一颗以root为根节点的不带前缀指针的字典树
{
node *p=root;
int i=0,index;
while(str[i])
{
index=str[i]-'A';//视具体情况改动
if(p->next[index]==NULL)
p->next[index]=new node();
p=p->next[index];
i++;
}
p->count=1;
}
//2.建立前缀指针,形成Tries图
void build_ac_automation(node *root)
//在建好的字典树上添加前缀指针,形成Tries图,即ac自动机
{
int i;
root->fail=NULL;
q[head++]=root;
while(head!=tail)
{
node *temp=q[tail++];
node *p=NULL;
for(i=0;i<kind;i++)
{
if(temp->next[i]!=NULL)
{
if(temp==root)
temp->next[i]->fail=root;
else
{
p=temp->fail;
while(p!=NULL)
{
if(p->next[i]!=NULL)
{
temp->next[i]->fail=p->next[i];
break;
}
p=p->fail;
}
if(p==NULL)
temp->next[i]->fail=root;
}
q[head++]=temp->next[i];
}
}
}
}
//3.查询母串
int query(node *root,char *s)
//有多少种模式串出现在母串str[]中
{
int i=0,cnt=0,index,len=strlen(s);
node *p=root;
while(s[i])
{
index=s[i]-'A';//视具体情况改动
while(p->next[index]==NULL && p!=root)
p=p->fail;
p=p->next[index];
p=(p==NULL)?root:p;
node *temp=p;
while(temp!=root&&temp->count)
{
cnt+=temp->count;
temp->count=0;
temp=temp->fail;
}
i++;
}
return cnt;
}
//4.初始化
void init()
{
head=tail=0;
Root=new node;
}
//************************************************************
后缀数组
//*****************************************************************
const int MAXN=100000+100;
char str[MAXN];//待处理字符串
int sa[MAXN];//求得的后缀数组
int wa[MAXN],wb[MAXN],wv[MAXN],wh[MAXN];
int cmp(int *r,int a,int b,int l)
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}
//求后缀数组sa[],下标1到n-1(此处n=strlen(str)+1)有效后缀
//将str的n个后缀从小到大进行排序之后把排好序的后缀的开头位置顺次放入sa中。
//保证Suffix(sa[i])<Suffix(sa[i+1])
//1<=i<n,sa[0]存放人为添加在末尾的那个最小的后缀
//倍增算法的时间复杂度为O(nlogn)
//倍增算法的空间复杂度都是O(n)
void da(char *r,int *sa,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=0;i<m;i++) wh[i]=0;
for(i=0;i<n;i++) wh[x[i]=r[i]]++;
for(i=1;i<m;i++) wh[i]+=wh[i-1];
for(i=n-1;i>=0;i--) sa[--wh[x[i]]]=i;
for(j=1,p=1;p<n;j*=2,m=p)
{
for(p=0,i=n-j;i<n;i++) y[p++]=i;
for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
for(i=0;i<n;i++) wv[i]=x[y[i]];
for(i=0;i<m;i++) wh[i]=0;
for(i=0;i<n;i++) wh[wv[i]]++;
for(i=1;i<m;i++) wh[i]+=wh[i-1];
for(i=n-1;i>=0;i--) sa[--wh[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
return;
}
int rank[MAXN],height[MAXN];
//定义height[i]=suffix(sa[i-1])和suffix(sa[i])的最长公
//共前缀,也就是排名相邻的两个后缀的最长公共前缀
//任意两个起始位置为i,j(假设rank[i]<rank[j])的后缀的最长公共前缀
//为height[rank[i]+1]、height[rank[i]+2]…height[rank[j]]的最小值
void calheight(char *r,int *sa,int n)
{
int i,j,k=0;
for(i=1;i<=n;i++) rank[sa[i]]=i;
for(i=0;i<n;height[rank[i++]]=k)
for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
return;
}
//求不可重叠最长重复子串
//先二分答案,把题目变成判定性问题:判断是否
//存在两个长度为k 的子串是相同的,且不重叠
//时间复杂度为O(N*log(N))
int Bin_Solve(int n)
{
int i,Minlen,Maxlen,midlen,lowid,highid;
bool flag;
Minlen=0,Maxlen=n/2;
while(Minlen<=Maxlen)//二分重复字串的长度
{
midlen=(Minlen+Maxlen)/2;
lowid=n+1,highid=0;
flag=false;
for(i=1;i<=n&&!flag;i++)//此处i表示排名
{
if (height[i]<midlen)lowid=highid=sa[i];//中间有一个的长度不大于midlen,就断开了
//即说明前面的和后面的最长公共前缀不可能大于等于miflen
else if(height[i]>=midlen)//长度大于等于midlen
{
lowid=min(lowid,sa[i]);
highid=max(highid,sa[i]);
if(highid-lowid>=midlen)//且不重叠
flag=true;
}
}
if(flag)Minlen=midlen+1;
else Maxlen=midlen-1;
}
return Maxlen<4?0:Maxlen+1;
}
//*****************************************************************
4.图论
邻接表建图
//*****************************************************
const int MAXN=1000+100;
const int INF = 0x3f3f3f3f;
int head[MAXN];
int dis[MAXN];
bool visited[MAXN];
int qq[MAXN];//模拟队列
struct node
{
int to;
int next;
}Edg[20000+100];
int n,m,tot;
void init()
{
tot=0;
memset(head,-1,sizeof(head));
}
void add(int a,int b)
{
Edg[tot].to=b;
Edg[tot].next=head[a];
head[a]=tot++;
}
void BFS(int s)
{
//queue<int>qq;
//qq.push(s);
int front,rear;
front=rear=0;
qq[front++]=s;
int now,i,to;
for(i=1;i<=n;i++)
{
if(i!=s)dis[i]=INF;
else dis[i]=0;
}
visited[s]=true;
while(rear<front)
{
//now=qq.front();
//qq.pop();
now=qq[rear++];
for(i=head[now];i!=-1;i=Edg[i].next)
{
to=Edg[i].to;
if(to==now||visited[to])continue;
dis[to]=dis[now]+1;
//qq.push(to);
qq[front++]=to;
visited[to]=true;
}
}
}
//*****************************************************
二分图:
模板一:匈牙利算法
//************************************************************
//二分图匹配(匈牙利算法的DFS实现)
//初始化:g[][]两边顶点的划分情况
//建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配
//g没有边相连则初始化为0
//uN是匹配左边的顶点数,vN是匹配右边的顶点数
//调用:res=hungary();输出最大匹配数
//优点:适用于稠密图,DFS找增广路,实现简洁易于理解
//时间复杂度:O(VE)
//************************************************************
//顶点编号从0开始的
const int MAXN=510;
int uN,vN;//u,v数目
int g[MAXN][MAXN];
int linker[MAXN];
bool visited[MAXN];
bool dfs(int u)//从左边开始找增广路径
{
int v;
for(v=0;v<vN;v++)//这个顶点编号从0开始,若要从1开始需要修改
if(g[u][v]&&!visited[v])
{
visited[v]=true;
if(linker[v]==-1||dfs(linker[v]))
{//找增广路,反向
linker[v]=u;
return true;
}
}
return false;//这个不要忘了,经常忘记这句
}
int hungary()
{
int res=0;
int u;
memset(linker,-1,sizeof(linker));
for(u=0;u<uN;u++)
{
memset(visited,0,sizeof(visited));
if(dfs(u)) res++;
}
return res;
}
//****************************************************
最小生成树的Kruskal算法
//************************************************************
//带权值的无向图的最小生成树的Kruskal算法
int id[maxm];//id[]存放边的下标,从[0-m-1]
int eu[maxm],ev[maxm],ew[maxm];//依次存放无向边的两顶点和权值
int n,m;//n为顶点数,m为边数
int par[maxn];//并查集中存放顶点的数组
int cmp( const int &i , const int &j)
{
return ew[i]<ew[j];
}
int Get_Par( int x)
{
if( par[x]==x)
return par[x];
par[x]=Get_Par(par[x]) ;
return par[x] ;
}
int Kruskal( )
{
int ret=0, i , j , p ;
for(i=1;i<=n;i++)
par[i]=i; // node [ 1 . . n ]
for(i=0;i<m;i++)
id[i]=i ; // ew [ 0 . .m.1]
std::sort(id,id+m,cmp) ;
for(j=-1,i=1;i<n;i++)
{
while(p=id[++j],Get_Par(eu[p])==Get_Par(ev[p]));
ret+=ew[p];
par[Get_Par(ev[p])]=Get_Par(eu[p]) ;
}
return ret;
}
//************************************************************
Dijkstra算法
//************************************************************
#define INF 0x3f3f3f3f
#define Max 155
int n;//表示顶点数目
int dis[Max];//dis数组表示源点到各点的距离
int g[Max][Max];//用邻接矩阵g[][]存放图的边
bool visited[Max];//标记原点到该点的最短距离是否找到
//开始应初始化memset(g, INF, sizeof(g));
void Dijkstra(int start)
{
int temp,k,i,j;
memset(visited,false,sizeof(visited));
for(i=1;i<=n;++i)
dis[i]=g[start][i];
dis[start]=0;
visited[start]=1;
for(i=1;i<=n;++i)
{
temp=INF;
for(int j=1;j<=n;++j)
if(!visited[j]&&temp>dis[j])
temp=dis[k=j];
if(temp==INF)break;
visited[k]=1;
for(j=1;j<=n;++j)
if(!visited[j]&&dis[j]>dis[k]+g[k][j])
dis[j]=dis[k]+g[k][j];
}
}
//************************************************************
flyod算法
//*****************************************************************
int const Max=1001;
int a[Max][Max];//存放边的权值
int b[Max];//存放顶点的权值
int nex[Max][Max]; //next[i][j]用来保存i-->j的最短路径中i的最优后即最近的顶点
int N;
void floyd()
//flyod算法求个顶点间的最短路径长度并记录路径
{
int i,j,k,fee;
for(i=1;i<=N;i++)
for(j=1;j<=N;j++)
nex[i][j]=j;
for(k=1;k<=N;k++)
{
for(i=1;i<=N;i++)
{
if(i==k||a[i][k]==-1)
continue;
for(j=1;j<=N;j++)
{
if(a[k][j]==-1||j==k)
continue;
fee = a[i][k]+a[k][j]+b[k];
if(a[i][j]==-1||a[i][j]>fee)
{
a[i][j]=fee;
nex[i][j]=nex[i][k];
}
//选择字典序小的路径
else if(a[i][j]==fee)
{
if(nex[i][j]>nex[i][k])
nex[i][j]=nex[i][k];
}
}
}
}
}
void path(int i, int j)
//递归输出最短路径
{
if(j==nex[i][j])
{
printf("%d-->%d\n",i,j);
}
else
{
printf("%d-->",i);
path(nex[i][j],j);
}
}
//*****************************************************************
数论
矩阵快速幂
//*****************************************************
//origin存放需计算的矩阵,res存放答案矩阵
//最终答案为res.a[1][0](对应f
)
struct matrix
{
__int64 a[2][2];
};
matrix origin,res;
//将res初始化为初始条件矩阵,人为输入关系递推矩阵origin
void init()
{
origin.a[0][0]=1,origin.a[1][0]=origin.a[0][1]=1,origin.a[1][1]=0;
res.a[0][0]=1,res.a[0][1]=res.a[1][0]=res.a[1][1]=0;
}
//直接将2个矩阵相乘x*y,返回计算后的矩阵
matrix multiply(matrix &x,matrix &y,__int64 MOD)
{
matrix temp;
memset(temp.a,0,sizeof(temp.a));
for(int i=0;i<2;i++)
{
for(int j=0;j<2;j++)
{
for(int k=0;k<2;k++)
{
temp.a[i][j]+=x.a[i][k]*y.a[k][j];
temp.a[i][j]%=MOD;
}
}
}
return temp;
}
//矩阵快速幂的计算,矩阵的n次幂,每个中间结果对MOD取模
void calc(matrix &origin,matrix &res,__int64 n,__int64 MOD)
{
while(n)
{
if(n&1)
res=multiply(origin,res,MOD);
n>>=1;
origin=multiply(origin,origin,MOD);
}
}
//*****************************************************
快速幂
A^B %C=A^( B%phi(C)+phi(C) ) %C B>=phi(C)
//************************************************************
//快速幂x^n%mod的计算
__int64 optimized_pow_n(__int64 x, __int64 n)
{
__int64 pw = 1;
while (n > 0)
{
if (n & 1)
pw *= x;
pw=pw%mod;
x *= x;
x=x%mod;
n >>= 1;
}
return pw;
}
//************************************************************
三分法求凹凸函数的极值点
//*****************************************************
//当需要求某凸性或凹形函数的极值,通过函数本身表达式并不容易求解时,就可以用三分法不断逼近求解
double mid, midmid;
while ( low + eps < high )
{
mid = (low + high) / 2;
midmid = (mid + high ) / 2;
double cmid = cal(mid);
double cmidmid = cal(midmid);
if ( cmid > cmidmid )
high = midmid;
else
low = mid;
}
//*****************************************************
普通母函数
//*****************************************************
//普通母函数,求组合数。
int n,sum;//n表示物品种类数,sum为在[1,sum]范围类求每个价值的组合数(不排列)
int num[100+10],value[100+10];//num[]存放该种类对应的个数,value[]存放该种类对应的价值
int a[10000+100];
int b[10000+100];
void Generating_function()
{
int i,j,k;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
a[0]=1;
for(i=1;i<=n;i++)
{
for(j=0;j<=sum;j++)
{
for(k=0;k<=num[i]&&k*value[i]+j<=sum;k++)
{
b[k*value[i]+j]+=a[j];
b[k*value[i]+j]%=10000;
}
}
memcpy(a,b,sizeof(b));
memset(b,0,sizeof(b));
}
}
//*****************************************************
最大公约数
//*****************************************************
//求a,b的最大公约数
LL Gcd(LL a,LL b)
{
return b==0?a:Gcd(b,a%b);
}
//*****************************************************
计算几何
//求不共线三点的外接圆,利用圆心到三点距离相等联立方程求得
point GetCentre(point a,point b,point c)
{
double a1=b.x-a.x,b1=b.y-a.y,c1=(a1*a1+b1*b1)/2;
double a2=c.x-a.x,b2=c.y-a.y,c2=(a2*a2+b2*b2)/2;
double d=a1*b2-a2*b1;
point ret;
ret.x=a.x+(c1*b2-c2*b1)/d;
ret.y=a.y+(a1*c2-a2*c1)/d;
return ret;
}
头文件及宏定义
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include <numeric>
using namespace std;
const double eps(1e-8);
const int INF = 0x3f3f3f3f;
//优先级队列的两种定义方式
struct cmp//对应大顶堆
{
bool operator()(int a,int b)
{
return a<b;
}
};
priority_queue<int,vector<int>,cmp>Q;
struct node
{
int id;
bool operator < (const node& b)const
{
return id>b.id;
}
};
priority_queue<node>Q;
//set,multiset用法
struct cmp
{
bool operator ()(const node &a,const node &b)const
{
return a.nam<b.nam;//从小到大排序
}
};
multiset<node,cmp>MulSet;//存放未处理
相关文章推荐
- ACM_模板_Gale_Shapley算法(稳定婚姻)
- ACM 左偏树(模板)
- ACM Treap(模板)
- [ACM模板]SuffixArray后缀数组
- ACM模板-f_zyj.pdf
- acm 模板
- ACM模板 图论,Floyd 任意两点间最短路
- ACM模板 图论
- acm模板
- ACM计算几何模板
- ACM_模板_最小生成树
- ACM-高精度模板
- ACM网络流模板 最大流ISAP URAL1774
- ACM练级日志:Treap个人用模板
- ACM 模板--链接表 无向图
- ACM 模板列表
- [ACM] 最短路算法整理(bellman_ford , SPFA , floyed , dijkstra 思想,步骤及模板)
- ACM 输入输出模板
- ACM计算几何知识点详解和模板代码2
- ACM模板--几何(多边形)