A.数学
1.组合数的函数(快速幂)
int q_pow(int x,int n)
{
int res=1;
while(n)
{
if(n&1)
{
res=res*x%mod;
}
x=x*x%mod;
n>>=1;
}
return res;
}
int C(int n,int m)
{
if(m>n)return 0;
return (fac[n] * q_pow(fac[m], mod - 2) % mod * q_pow(fac[n - m], mod - 2) % mod)%mod ;
}
fac[0]=1;
fac[n]=fac[n-1]*n%mod;
1.1lucas求大组合数
C(n,m) = C(n%p,m%p) * C(n/p,m/p);
ll Lucas(ll n,ll m) {
if(m == 0) return 1;
return C(n%mod, m%mod) * Lucas(n/mod,m/mod)%mod;
}
2.素数筛
bool s[100015];
void solve()
{
s[1]=1;
for(int i=2;i*i<=100005;i++)
{
if(!s[i])
{
for(int j=i*i;j<=100005;j+=i)s[j]=1;
}
}
}
3.快速乘(会爆long long时,不写高精度的方法)
ll mul(ll a,ll b){
ll ans=0;
while(b){
if(b&1) ans=(ans+a)%mod;
a=a*2%mod;
b>>=1;
}
return ans;
}
4.拓展欧几里得 (求ax+by=gcd(a,b)的解 /求逆元)
LL ex_gcd(LL a,LL b,LL &x,LL &y)
{
if(b==0)
{
x=1;y=0;
return a;
}
LL r=ex_gcd(b,a%b,x,y);
LL t=x;
x=y;
y=t-a/b*y;
return r;
}
求逆元:A和MOD互质的时候才存在,否则不存在逆元
首先同余模定理如下:
(a+b)%c=(a%c+b%c)%c;
(ab)%c=(a%cb%c)%c;
也就是说对于取模的加减法,和乘法我们都可以运用同余模定理来进行计算,那么,对于除法我们应该怎么办呢?首先想到的就是把除法转换成乘法,然后就可以运用定理了。怎么转换呢,在普通乘法中,我们知道,除以一个数就等于乘上一个数的倒数,其实这个倒数就是我们所谓的逆元。A*(A的逆元)=单位元。在普通的乘法中
A的逆元就是它的倒数。
那么,在模n乘法中,我们应该怎么求逆元呢?我们设A的逆元为X,那么我们就可以得到(AX)%MOD=1(模n乘法的单位元也是1)。对这个式子进行变形
,就可以得到:
(AX)%MOD=1;那么肯定存在k使得AX=kMOD+1;
移项可得:AX-kMOD=1;
所以,当A和MOD互素时,就可以写成
AX-kMOD=gcd(A,MOD);
如果把A看做a,MOD看做b,X看做x,-k看做y的话,则上式可化为:
ax+by=gcd(a,b);
这样就可以用扩展欧几里得算法求出来x了,也就是我们要找的逆元。
5.费马小定理:
假如p是质数,且gcd(a,p)=1,那么 a^(p-1)≡1(mod p)
所以a*a^(p-2)≡1(mod p)
得出:a^(p-2)就是a的乘法逆元
利用快速幂可以计算出a^(p-2)
6.裴蜀定理
若ax+by=c,则c%(gcd(a,b))=0
7.中国剩余定理(同余方程组)
LL CRT(LL a[], LL m[], LL n)
{
LL M = 1;
for (int i = 0; i < n; i++) M *= m[i];
LL ret = 0;
for (int i = 0; i < n; i++)
{
LL x, y;
LL tm = M / m[i];
ex_gcd(tm, m[i], x, y);
ret = (ret + tm * x * a[i]) % M;
}
return (ret + M) % M;
}
8.欧拉函数 对于一个正整数n,小于n且和n互质的正整数(包括1)的个数
void euler()
{
E[1]=1;
for(int i=2;i<maxn;i++){
if(!E[i])
for(int j=i;j<maxn;j+=i){
if(!E[j])E[j]=j;
E[j]=E[j]/i*(i-1);
}
}
}
几个性质:
8.矩阵快速幂
#include <cstdio>
#include <iostream>
using namespace std;
long long x[999][999];
long long ans[999][999];
long long dx[999][999];
const int p=1e9+7;
void ans_cf(int n)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dx[i][j]=ans[i][j],ans[i][j]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
ans[i][j]=(ans[i][j]+(x[i][k]*dx[k][j])%p)%p;
}
void x_cf(int n)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dx[i][j]=x[i][j],x[i][j]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
x[i][j]=(x[i][j]+(dx[i][k]*dx[k][j])%p)%p;
}
void fast_pow(long long n,long long w)
{
while(w)
{
if(w%2==1) ans_cf(n);
w/=2;
x_cf(n);
}
}
int main()
{
long long n,k;
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&x[i][j]),ans[i][j]=x[i][j];
fast_pow(n,k-1);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
printf("%lld ",ans[i][j]);
puts("");
}
}
B.数据结构
1.链表
queue<int> q;//先假定线性队列,弹出最顶部值,根据要求插入队尾
q.pop();
if(必要なら)q.insert(x);
2.st表
int arr[max_n][m];
int ql,qr,ans;
for(int i=1;i<=n;i++)cin>>arr[i][0];
for(int i=1;i<=m;i++)
for(int j=1;j+(1<<i)-1<=n;j++)
arr[i][j]=max(arr[j][i-1],arr[j+(1<<(i-1))][j-1];
int k=log2(qr-ql+1);
ans=max/min(arr[l][k],arr[r-(1<<k)+1][k]);
3.并查集
int fins(int u)
{
if(f[u]==u)return u;
return f[u]=fins(f[u]);
}
void merg(int x,int y)
{
x=fins(x);y=fins(y);
if(x!=y)f[x]=y;
}
4.线段树
inline int ls(int x)
{
return x<<1;
}
inline int rs(int x)
{
return x<<1|1;
}
void scan()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
}
inline void push_up(int p)
{
ans[p]=ans[ls(p)]+ans[rs(p)];
}
void build(int p,int l,int r)
{
tag[p]=0;
if(l==r){ans[p]=a[l];return ;}
int mid=(l+r)>>1;
build(ls(p),l,mid);
build(rs(p),mid+1,r);
push_up(p);
}
inline void f(int p,int l,int r,int k)
{
tag[p]=tag[p]+k;
ans[p]=ans[p]+k*(r-l+1);
}
inline void push_down(int p,int l,int r)
{
int mid=(l+r)>>1;
f(ls(p),l,mid,tag[p]);
f(rs(p),mid+1,r,tag[p]);
tag[p]=0;
}
inline void update(int nl,int nr,int l,int r,int p,int k)
{
if(nl<=l&&r<=nr)
{
ans[p]+=k*(r-l+1);
tag[p]+=k;
return ;
}
push_down(p,l,r);
int mid=(l+r)>>1;
if(nl<=mid)update(nl,nr,l,mid,ls(p),k);
if(nr>mid) update(nl,nr,mid+1,r,rs(p),k);
push_up(p);
}
int query(int q_x,int q_y,int l,int r,int p)
{
int res=0;
if(q_x<=l&&r<=q_y)return ans[p];
int mid=(l+r)>>1;
push_down(p,l,r);
if(q_x<=mid)res+=query(q_x,q_y,l,mid,ls(p));
if(q_y>mid) res+=query(q_x,q_y,mid+1,r,rs(p));
return res;
}
4.1加乘线段树(模块)
struct node{
long long v, mul, add;
}st[400007];
//buildtree
void bt(int root, int l, int r){
//初始化lazytag
st[root].mul=1;
st[root].add=0;
if(l==r){
st[root].v=a[l];
}
else{
int m=(l+r)/2;
bt(root*2, l, m);
bt(root*2+1, m+1, r);
st[root].v=st[root*2].v+st[root*2+1].v;
}
st[root].v%=p;
return ;
}
//核心代码,维护lazytag
void pushdown(int root, int l, int r){
int m=(l+r)/2;
//根据我们规定的优先度,儿子的值=此刻儿子的值*爸爸的乘法lazytag+儿子的区间长度*爸爸的加法lazytag
st[root*2].v=(st[root*2].v*st[root].mul+st[root].add*(m-l+1))%p;
st[root*2+1].v=(st[root*2+1].v*st[root].mul+st[root].add*(r-m))%p;
//很好维护的lazytag
st[root*2].mul=(st[root*2].mul*st[root].mul)%p;
st[root*2+1].mul=(st[root*2+1].mul*st[root].mul)%p;
st[root*2].add=(st[root*2].add*st[root].mul+st[root].add)%p;
st[root*2+1].add=(st[root*2+1].add*st[root].mul+st[root].add)%p;
//把父节点的值初始化
st[root].mul=1;
st[root].add=0;
return ;
}
//update1,乘法,stdl此刻区间的左边,stdr此刻区间的右边,l给出的左边,r给出的右边
void ud1(int root, int stdl, int stdr, int l, int r, long long k){
//假如本区间和给出的区间没有交集
if(r<stdl || stdr<l){
return ;
}
//假如给出的区间包含本区间
if(l<=stdl && stdr<=r){
st[root].v=(st[root].v*k)%p;
st[root].mul=(st[root].mul*k)%p;
st[root].add=(st[root].add*k)%p;
return ;
}
//假如给出的区间和本区间有交集,但是也有不交叉的部分
//先传递lazytag
pushdown(root, stdl, stdr);
int m=(stdl+stdr)/2;
ud1(root*2, stdl, m, l, r, k);
ud1(root*2+1, m+1, stdr, l, r, k);
st[root].v=(st[root*2].v+st[root*2+1].v)%p;
return ;
}
//update2,加法,和乘法同理
void ud2(int root, int stdl, int stdr, int l, int r, long long k){
if(r<stdl || stdr<l){
return ;
}
if(l<=stdl && stdr<=r){
st[root].add=(st[root].add+k)%p;
st[root].v=(st[root].v+k*(stdr-stdl+1))%p;
return ;
}
pushdown(root, stdl, stdr);
int m=(stdl+stdr)/2;
ud2(root*2, stdl, m, l, r, k);
ud2(root*2+1, m+1, stdr, l, r, k);
st[root].v=(st[root*2].v+st[root*2+1].v)%p;
return ;
}
4.2动态开点
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=2e5+5;
typedef int LL;
inline LL read(){LL x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;}
struct Tree{
LL l,r,tag,val;
LL lson=0,rson=0;
}tree[maxn*32];
LL tot=0;
LL newTree(LL now,LL last,LL l,LL r){
tree[now].l=l;tree[now].r=r;
//tree[now].tag=tree[last].tag;///继承父亲的tag
tree[now].val=1;
//tree[now].lson=tree[now].rson=0;
/// debug(now);
return now;
}
void addtag(LL now,LL d){
tree[now].tag+=d;
tree[now].val+=d;
//cout<<tree[now].l<<tree[now].r<<" "<<tree[now].tag<<"\n";
}
void push_down(LL now){
if(tree[now].tag){
LL l=tree[now].l;LL r=tree[now].r;
LL mid=(l+r)>>1;
if(!tree[now].lson) tree[now].lson=newTree(++tot,now,l,mid);
addtag(tree[now].lson,tree[now].tag);
if(!tree[now].rson) tree[now].rson=newTree(++tot,now,mid+1,r);
addtag(tree[now].rson,tree[now].tag);
tree[now].tag=0;
}
}
void push_up(LL now){
LL lmax=1;LL rmax=1;
if(tree[now].lson) lmax=tree[ tree[now].lson].val;
if(tree[now].rson) rmax=tree[ tree[now].rson].val;
tree[now].val=max(lmax,rmax);
}
LL times=0;
void update(LL now,LL l,LL r,LL d){
//cout<<tree[now].l<<" "<<tree[now].r<<"\n";
//if(now==0) now=++tot;
push_down(now);
if(l<=tree[now].l&&r>=tree[now].r){
addtag(now,d);
return;
}
LL mid=(tree[now].l+tree[now].r)>>1;
// times++;
// debug(mid);
/* if(times>=10){
return;
}*/
//if(tree[now].l!=tree[now].r){
if(l<=mid){
if(!tree[now].lson) tree[now].lson=newTree(++tot,now,tree[now].l,mid);
update(tree[now].lson,l,r,d);
}
if(r>mid){
if(!tree[now].rson) tree[now].rson=newTree(++tot,now,mid+1,tree[now].r);
update(tree[now].rson,l,r,d);
}
//}
push_up(now);
}
LL query(LL now,LL l,LL r){
//if( (!tree[now].l) && (!tree[now].r) ) return tree[now].val;
//cout<<tree[now].l<<" "<<tree[now].r<<" "<<tree[now].val<<"\n";
if(now==0) return 0;
push_down(now);
if(l<=tree[now].l&&r>=tree[now].r){
return tree[now].val;
}
LL mid=(tree[now].l+tree[now].r)>>1;
LL ans=1;
if(l<=mid) ans=max(ans,query(tree[now].lson,l,r));
if(r>mid) ans=max(ans,query(tree[now].rson,l,r));
return ans;
}
int main(void){
/// cin.tie(0);std::ios::sync_with_stdio(false);
LL n,m;cin>>n>>m;
tree[++tot].l=1;tree[tot].r=n;
tree[tot].lson=tree[tot].rson=0;
tree[tot].val=1;tree[tot].tag=0;///默认高度是1
//cout<<"idiocy"<<"\n";
for(LL i=1;i<=m;i++){
LL op;cin>>op;
if(op==1){
LL l,r,x;cin>>l>>r>>x;
update(1,l,r,x);
}
else if(op==2){
LL l,r;cin>>l>>r;
cout<<query(1,l,r)<<"\n";
}
}
}
5.树状数组
int n;
int a[1005],c[1005]; //对应原数组和树状数组
int lowbit(int x){
return x&(-x);
}
void updata(int i,int k){ //在i位置加上k
while(i <= n){
c[i] += k;
i += lowbit(i);
}
}
int getsum(int i){ //求A[1 - i]的和
int res = 0;
while(i > 0){
res += c[i];
i -= lowbit(i);
}
return res;
}
5.0区间查找 getsum(y)-getsum(x-1)
5.1 区间修改
差分存储 因此如果改变区间[x,y]只需要更新x-1和y
int n;
int a[1005],c[1005]; //对应原数组和树状数组
int lowbit(int x){
return x&(-x);
}
void updata(int i,int k){ //在i位置加上k
while(i <= n){
c[i] += k;
i += lowbit(i);
}
}
int getsum(int i){ //求A[1 - i]的和
int res = 0;
while(i > 0){
res += c[i];
i -= lowbit(i);
}
return res;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
update(i,a[i]-a[i-1]);
}
int k,l,r,p;
cin>>l>>r>>k;
update(l,k);
update(r+1,-k);
cin>>p;
cout<<getsum(p)<<endl;//求一点
}
5.1.1区间修改 求区间和
void updata(int i,int k){
int x = i; //因为x不变,所以得先保存i值
while(i <= n){
sum1[i] += k;
sum2[i] += k * (x-1);
i += lowbit(i);
}
}
int getsum(int i){ //求前缀和
int res = 0, x = i;
while(i > 0){
res += x * sum1[i] - sum2[i];
i -= lowbit(i);
}
return res;
}
Σ[x,y]=getsum(y)-getsum(x-1);
C.算法
1.二分
//可取r时
while(l<=r)
mid=l+(r-l)/2; //防爆
if(?)l=mid+1;
else r=mid-1;
不可取r时
while(l<r)
mid=l+(r-l)/2;
if(?)l=mid+1;
else r=mid;
1.1 浮点数二分
while(r - l > 1e-8) {
double mid = (l + r) / 2;
if(?) {
r = mid;
} else {
l = mid;
}
}
1.2三分
int l = 1,r = 100;
while(l < r) {
int lmid = l + (r - l) / 3;
int rmid = r - (r - l) / 3;
lans = f(lmid),rans = f(rmid);
// 求凹函数的极小值
if(lans <= rans) r = rmid - 1;
else l = lmid + 1;
// 求凸函数的极大值
if(lasn >= rans) l = lmid + 1;
else r = rmid - 1;
}
const double EPS = 1e-9;
while(r - l < EPS) {
double lmid = l + (r - l) / 3;
double rmid = r - (r - l) / 3;
lans = f(lmid),rans = f(rmid);
// 求凹函数的极小值
if(lans <= rans) r = rmid;
else l = lmid;
// 求凸函数的极大值
if(lans >= rans) l = lmid;
else r = rmid;
2.用于有序数组,数组的标号按升序/降序排列
其实用个vector sort一下就行了,就保留下来看看在sort里写方法的过程吧
只能用于有序,似乎没啥用
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin>>n;
vector<int> a(n);
vector<int> b(n);
for(int i=0;i<n;i++)b[i]=i;
for(int i=0;i<n;i++)cin>>a[i];
sort(b.begin(),b.end(),[&](int i,int j){
return a[i]>a[j];
});
for(auto &it:b)cout<<it<<" ";
cout<<endl;
}
4.分块 莫队
总共n个元素 分为sqrt(n)块
int v[N],blocksize=0;
struct query{
int l,r,id,bl;
}q[N];
int sum[N];
bool ans[N];
int cnt=0;
inline void add(register int x)
{
if(++sum[v[x]]==1)
++cnt;
}
inline void del(register int x)
{
if(--sum[v[x]]==0)
--cnt;
}
inline bool cmp(register query a,register query b)
{
return a.bl!=b.bl?a.l<b.l:((a.bl&1)?a.r<b.r:a.r>b.r);
}
int main()
{
memset(sum,0,sizeof(sum));
int n=read(),m=read();
blocksize=sqrt(n);
for(register int i=1;i<=n;++i)
v[i]=read();
for(register int i=1;i<=m;++i)
{
int l=read(),r=read();
q[i]=(query){l,r,i,(l-1)/blocksize+1};
}
sort(q+1,q+m+1,cmp);
int l=1,r=0;
for(register int i=1;i<=m;++i)
{
int ll=q[i].l,rr=q[i].r;
while(l<ll)
del(l++);
while(l>ll)
add(--l);
while(r<rr)
add(++r);
while(r>rr)
del(r--);
ans[q[i].id]=(cnt==rr-ll+1)?1:0;
}
for(register int i=1;i<=m;++i)
if(ans[i])
puts("Yes");
else
puts("No");
return 0;
}
5.分层图dij
struct edge
{
int u,v,w;
}e[N];
int head[N],len=0,vis[N],dis[N];
void add(int u,int v,int w)
{
e[++len]={head[u],v,w};
head[u]=len;
}
void dij(int s)
{
priority_queue<pii,vector<pii>,greater<pii> > q;
q.push(mp(0,s));
while(!q.empty())
{
int i,pre=q.top().se;
q.pop();
if(vis[pre])
continue;
vis[pre]=1;
for(i=head[pre];i;i=e[i].u)
{
int v=e[i].v,w=e[i].w;
if(dis[v]>dis[pre]+w)
{
dis[v]=dis[pre]+w;
q.push(mp(dis[v],v));
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int n,m,i,k,j,s,t;
cin>>n>>m>>k>>s>>t;
s++;
t++;
for(i=1;i<=n*(k+1);i++)
dis[i]=inf;
dis[s]=0;
for(i=1;i<=m;i++)
{
int u,v,w;
cin>>u>>v>>w;
u++;
v++;
add(u,v,w);
add(v,u,w);
for(j=1;j<=k;j++)
{
add(u+j*n,v+j*n,w);
add(v+j*n,u+j*n,w);
add(u+(j-1)*n,v+j*n,0);
add(v+(j-1)*n,u+j*n,0);
}
}
dij(s);
int ans=dis[t];
for(i=1;i<=k;i++)
ans=min(ans,dis[t+i*n]);
cout<<ans<<endl;
return 0;
}
6.字符串的若干算法
6.1字典树
struct trie {
int nex[100000][26], cnt;
bool exist[100000]; // 该结点结尾的字符串是否存在
void insert(char *s, int l) { // 插入字符串
int p = 0;
for (int i = 0; i < l; i++) {
int c = s[i] - 'a';
if (!nex[p][c]) nex[p][c] = ++cnt; // 如果没有,就添加结点
p = nex[p][c];
}
exist[p] = 1;
}
bool find(char *s, int l) { // 查找字符串
int p = 0;
for (int i = 0; i < l; i++) {
int c = s[i] - 'a';
if (!nex[p][c]) return 0;
p = nex[p][c];
}
return exist[p];
}
};
6.2 kmp
char a1[2000000],a2[2000000];
int kmp[2000000];
int main()
{
scanf("%s%s",a1,a2);
kmp[0]=kmp[1]=0;//前一位,两位失配了,都只可能将第一位作为新的开头
int len1=strlen(a1),len2=strlen(a2);
int k;
k=0;
for(int i=1;i<len2;i++)//自己匹配自己
{
while(k&&a2[i]!=a2[k])k=kmp[k];//找到最长的前后缀重叠长度
kmp[i+1]=(a2[i]==a2[k])?++k:0;//不相等的情况,即无前缀能与后缀重叠,直接赋值位0(注意是给下一位,因为匹配的是下一位适失配的情况)
}
k=0;
for(int i=0;i<len1;i++)
{
while(k&&a1[i]!=a2[k])k=kmp[k];//如果不匹配,则将利用kmp数组往回跳
k+=(a1[i]==a2[k])?1:0;//如果相等了,则匹配下一位
if(k==len2)printf("%d\n",i-len2+2);//如果已经全部匹配完毕,则输出初始位置
}
for(int i=1;i<=len2;i++)printf("%d ",kmp[i]);//输出f数组
6.3 ac自动机
#include<bits/stdc++.h>
#define maxn 1000001
using namespace std;
struct kkk{
int son[26],flag,fail;
}trie[maxn];
int n,cnt;
char s[1000001];
queue<int >q;
void insert(char* s){
int u=1,len=strlen(s);
for(int i=0;i<len;i++){
int v=s[i]-'a';
if(!trie[u].son[v])trie[u].son[v]=++cnt;
u=trie[u].son[v];
}
trie[u].flag++;
}
void getFail(){
for(int i=0;i<26;i++)trie[0].son[i]=1; //初始化0的所有儿子都是1
q.push(1);trie[1].fail=0; //将根压入队列
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;i++){ //遍历所有儿子
int v=trie[u].son[i]; //处理u的i儿子的fail,这样就可以不用记父亲了
int Fail=trie[u].fail; //就是fafail,trie[Fail].son[i]就是和v值相同的点
if(!v){trie[u].son[i]=trie[Fail].son[i];continue;} //不存在该节点,第二种情况
trie[v].fail=trie[Fail].son[i]; //第三种情况,直接指就可以了
q.push(v); //存在实节点才压入队列
}
}
}
int query(char* s){
int u=1,ans=0,len=strlen(s);
for(int i=0;i<len;i++){
int v=s[i]-'a';
int k=trie[u].son[v]; //跳Fail
while(k>1&&trie[k].flag!=-1){ //经过就不统计了
ans+=trie[k].flag,trie[k].flag=-1; //累加上这个位置的模式串个数,标记已经过
k=trie[k].fail; //继续跳Fail
}
u=trie[u].son[v]; //到下一个儿子
}
return ans;
}
int main(){
cnt=1; //代码实现细节,编号从1开始
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",s);
insert(s);
}
getFail();
scanf("%s",s);
printf("%d\n",query(s));
return 0;
}
7.高精度板子
int compare(string str1,string str2)
{
if(str1.length()>str2.length()) return 1;
else if(str1.length()<str2.length()) return -1;
else return str1.compare(str2);
}
//高精度加法
//只能是两个正数相加
string add(string str1,string str2)//高精度加法
{
string str;
int len1=str1.length();
int len2=str2.length();
//前面补0,弄成长度相同
if(len1<len2)
{
for(int i=1;i<=len2-len1;i++)
str1="0"+str1;
}
else
{
for(int i=1;i<=len1-len2;i++)
str2="0"+str2;
}
len1=str1.length();
int cf=0;
int temp;
for(int i=len1-1;i>=0;i--)
{
temp=str1[i]-'0'+str2[i]-'0'+cf;
cf=temp/10;
temp%=10;
str=char(temp+'0')+str;
}
if(cf!=0) str=char(cf+'0')+str;
return str;
}
//高精度减法
//只能是两个正数相减,而且要大减小
/*string sub(string str1,string str2)//高精度减法
{
string str;
int tmp=str1.length()-str2.length();
int cf=0;
for(int i=str2.length()-1;i>=0;i--)
{
if(str1[tmp+i]<str2[i]+cf)
{
str=char(str1[tmp+i]-str2[i]-cf+'0'+10)+str;
cf=1;
}
else
{
str=char(str1[tmp+i]-str2[i]-cf+'0')+str;
cf=0;
}
}
for(int i=tmp-1;i>=0;i--)
{
if(str1[i]-cf>='0')
{
str=char(str1[i]-cf)+str;
cf=0;
}
else
{
str=char(str1[i]-cf+10)+str;
cf=1;
}
}
str.erase(0,str.find_first_not_of('0'));//去除结果中多余的前导0
return str;
}
//高精度乘法
//只能是两个正数相乘
string mul(string str1,string str2)
{
string str;
int len1=str1.length();
int len2=str2.length();
string tempstr;
for(int i=len2-1;i>=0;i--)
{
tempstr="";
int temp=str2[i]-'0';
int t=0;
int cf=0;
if(temp!=0)
{
for(int j=1;j<=len2-1-i;j++)
tempstr+="0";
for(int j=len1-1;j>=0;j--)
{
t=(temp*(str1[j]-'0')+cf)%10;
cf=(temp*(str1[j]-'0')+cf)/10;
tempstr=char(t+'0')+tempstr;
}
if(cf!=0) tempstr=char(cf+'0')+tempstr;
}
str=add(str,tempstr);
}
str.erase(0,str.find_first_not_of('0'));
return str;
}
//高精度除法
//两个正数相除,商为quotient,余数为residue
//需要高精度减法和乘法
void div(string str1,string str2,string "ient,string &residue)
{
quotient=residue="";//清空
if(str2=="0")//判断除数是否为0
{
quotient=residue="ERROR";
return;
}
if(str1=="0")//判断被除数是否为0
{
quotient=residue="0";
return;
}
int res=compare(str1,str2);
if(res<0)
{
quotient="0";
residue=str1;
return;
}
else if(res==0)
{
quotient="1";
residue="0";
return;
}
else
{
int len1=str1.length();
int len2=str2.length();
string tempstr;
tempstr.append(str1,0,len2-1);
for(int i=len2-1;i<len1;i++)
{
tempstr=tempstr+str1[i];
tempstr.erase(0,tempstr.find_first_not_of('0'));
if(tempstr.empty())
tempstr="0";
for(char ch='9';ch>='0';ch--)//试商
{
string str,tmp;
str=str+ch;
tmp=mul(str2,str);
if(compare(tmp,tempstr)<=0)//试商成功
{
quotient=quotient+ch;
tempstr=sub(tempstr,tmp);
break;
}
}
}
residue=tempstr;
}
quotient.erase(0,quotient.find_first_not_of('0'));
if(quotient.empty()) quotient="0";
}
8.凸包
#include<bits/stdc++.h>
using namespace std;
#define N 100010
struct Node{
double x,y;
}a[N],p[N];
int n,top=1;
double cross(Node p0,Node p1,Node p2){
return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
double dis(Node p1,Node p2){
return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
bool cmp(Node p1,Node p2){
double z=cross(a[0],p1,p2);
return (z>0||(z==0&&dis(a[0],p1)<dis(a[0],p2)));
}
void Graham(){
int k=0;
for(int i=0;i<n;i++)
if(a[i].y<a[k].y||(a[i].y==a[k].y&&a[i].x<a[k].x))k=i;
swap(a[0],a[k]);
sort(a+1,a+n,cmp);
p[0]=a[0];
p[1]=a[1];
for(int i=2;i<n;i++){
while(cross(p[top-1],p[top],a[i])<0&&top)top--;
p[++top]=a[i];
}
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%lf%lf",&a[i].x,&a[i].y);
Graham();
double ans;
for(int i=0;i<top;i++)ans+=dis(p[i],p[i+1]);
ans+=dis(p[0],p[top]);
printf("%.2lf",ans);
return 0;
}
6.tarjan缩点
//cyc
#pragma GCC optimize("Ofast")
#pragma GCC target("avx,avx2,fma")
#pragma GCC optimization ("unroll-loops")
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define mst(a) memset(a,0,sizeof a)
using namespace std;
typedef pair<int,int> pii;
const int maxn=1e4+5;
int val[maxn];
int n,m;
vector<int> rec[maxn];
vector<pii> rec0;
vector<int> rec1[maxn];
vector<int> aft[maxn];
int dfn[maxn];
int vis[maxn];
int stk[maxn];
int low[maxn];
int cnt,top;
int sd[maxn];
int dp[maxn];
int ins[maxn];
void tarjan(int x)
{
cnt++;top++;
low[x]=dfn[x]=cnt;
stk[top]=x;vis[x]=1;
for(int it:rec[x]){
if(!dfn[it]){
tarjan(it);
low[x]=min(low[x],low[it]);
}
else{
if(vis[it]){
low[x]=min(low[x],low[it]);
}
}
}
if(dfn[x]==low[x]){
int y;
while(y=stk[top--]){
sd[y]=x;
vis[y]=0;
if(x==y)break;
val[x]+=val[y];
}
}
}
void solve()
{
queue<int> que;
for(int i=1;i<=n;i++){
if(sd[i]==i&&!ins[i]){
que.push(i);
dp[i]=val[i];
}
}
while(!que.empty()){
int ft=que.front();
que.pop();
for(int it:rec1[ft]){
dp[it]=max(dp[it],dp[ft]+val[it]);
ins[it]--;
if(!ins[it])que.push(it);
}
}
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>val[i];
}
int u,v;
for(int i=1;i<=m;i++){
cin>>u>>v;
rec[u].pb(v);
rec0.pb({u,v});
}
for(int i=1;i<=n;i++){
if(!dfn[i])tarjan(i);
}
for(pii it:rec0){
int x,y;
x=sd[it.fi];y=sd[it.se];
if(x!=y){
rec1[x].pb(y);
ins[y]++;
}
}
solve();
int ans=0;
for(int i=1;i<=n;i++){
ans=max(ans,dp[i]);
}
cout<<ans<<endl;
}
割点
void tarjan(int u,int mr){
int rc=0;
dfn[u]=low[u]=++cnt;
for (int p=head[u];p;p=nxt[p]){
int v=a[p];
if (!dfn[v]){
tarjan(v,mr);
low[u]=min(low[u],low[v]);
if (low[v]>=dfn[u]&&u!=mr) cut[u]=true;
if (u==mr) rc++;
}
low[u]=min(low[u],dfn[v]);
}
if (u==mr&&rc>=2) cut[mr]=true;
}
7.最近公共祖先 倍增
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=500000+2;
int n,m,s;
int k=0;
int head[maxn],d[maxn],p[maxn][21];//head数组就是链接表标配了吧?d存的是深度(deep),p[i][j]存的[i]向上走2的j次方那么长的路径
struct node{
int v,next;
}e[maxn*2];//存树
void add(int u,int v)
{
e[k].v=v;
e[k].next=head[u];
head[u]=k++;
} //加边函数
void dfs(int u,int fa)
{
d[u]=d[fa]+1;
p[u][0]=fa;
for(int i=1;(1<<i)<=d[u];i++)
p[u][i]=p[p[u][i-1]][i-1];
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if(v!=fa)
dfs(v,u);
}
} //首先进行的预处理,将所有点的deep和p的初始值dfs出来
int lca(int a,int b) //非常标准的lca查找
{
if(d[a]>d[b])
swap(a,b); //保证a是在b结点上方,即a的深度小于b的深度
for(int i=20;i>=0;i--)
if(d[a]<=d[b]-(1<<i))
b=p[b][i]; //先把b移到和a同一个深度
if(a==b)
return a; //特判,如果b上来和就和a一样了,那就可以直接返回答案了
for(int i=20;i>=0;i--)
{
if(p[a][i]==p[b][i])
continue;
else
a=p[a][i],b=p[b][i]; //A和B一起上移
}
return p[a][0]; 找出最后a值的数字
}
int main()
{
memset(head,-1,sizeof(head));
int a,b;
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
add(a,b);
add(b,a); //无向图,要加两次
}
dfs(s,0);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
printf("%d\n",lca(a,b));
}
return 0;
}
8.数位dp
求含1个数的乘积,也可以遍历从1个到二进制有效最长的长度,分别用快速幂求出每部分的值
mst成-1能过 但是mst成0就过不了
十分神奇
```cpp
//cyc
#pragma GCC optimize("Ofast")
#pragma GCC target("avx,avx2,fma")
#pragma GCC optimization ("unroll-loops")
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define mst(a) memset(a,-1,sizeof a)
#define int long long
using namespace std;
typedef pair<int,int> pii;
/*
____ ____ _____ .____________ _____
/_ /_ | / | || ____/_ | / | |
| || |/ | ||____ \ | |/ | |_
| || / ^ / \| / ^ /
|___||___\____ /______ /|___\____ |
|__| \/ |__|
*/
const int mod = 10000007;
int a[63];
int dp[64][64];//pos cnt
int q_pow(int x,int n)
{
int res=1;
while(n)
{
if(n&1)
{
res=res*x%mod;
}
x=x*x%mod;
n>>=1;
}
return res;
}
int dfs(int pos,int now,int cnt,bool tp)//now 目前是1的位数 cnt目标1的个数
{
if(pos==-1){
return now==cnt;
}
if(!tp&&dp[pos][now]!=-1) return dp[pos][now];
int ret=0;
int bound=tp?a[pos]:1;
for(int i=0;i<=bound;i++){
ret+=dfs(pos-1,now+(i==1),cnt,tp&&(i==bound));
}
if(!tp)dp[pos][now]=ret;
return ret;
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int n;
cin>>n;
int c=0;
while(n){
a[c]=n&1;
n>>=1;
c++;
}
// cout<<c<<endl;
int ans=1;
for(int i=1;i<=c;i++){
mst(dp);
ans=ans*q_pow(i,dfs(c-1,0,i,1))%mod;
}
cout<<ans<<endl;
}