文章目录
- 字符串处理
- 后缀数组
- manacher
- hash
- KMP
- 最大最小表达法
- 数论
- 约瑟夫环
- 欧拉函数
- 莫比乌斯反演
- 逆序对
- 归并排序求逆序对
- 素数
- 线性筛
- 欧几里得与扩展欧几里得
- 欧几里得算法:
- 扩展欧几里得算法:
- 逆元
- 扩展欧几里得
- 费马小定理+欧拉定理
- 递推求逆元
- __int128高精度运算
- 唯一分解定理
- 数据结构
- 树链剖分
- 字典树
- 树状数组
- 二维树状数组
- 线段树
- 权值线段树
- 主席树
- 单调栈
- 图论
- 拓扑排序
- tarjan
- 求割点
- 二分图匹配--匈牙利算法
- 最优匹配
- 网络流问题
- Dinic
- KM算法
- 最短路径问题
- dijstra
- 搜索
- 动态规划
- 计算几何
- 其他算法
- 快速输入输出
- 快读
- 快输
- C++大数
- 大数加法
- 大数乘以整形数
- 大数除以整形数
- 大数乘法
- Java大数
- A == B
- 大整数加法
- 大数阶乘
- 大斐波那契数
- A/B
- 莫队算法
- 常规莫队
- 带修改莫队
字符串处理
后缀数组
全文背诵
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>using namespace std;const int N = 1000010;char s[N];
int n, sa[N], rk[N], oldrk[N << 1], id[N], px[N], cnt[N];
// px[i] = rk[id[i]](用于排序的数组所以叫 px)bool cmp(int x, int y, int w) {return oldrk[x] == oldrk[y] && oldrk[x + w] == oldrk[y + w];
}int main() {int i, m = 300, p, w;scanf("%s", s + 1);n = strlen(s + 1);for (i = 1; i <= n; ++i) ++cnt[rk[i] = s[i]];for (i = 1; i <= m; ++i) cnt[i] += cnt[i - 1];for (i = n; i >= 1; --i) sa[cnt[rk[i]]--] = i;for (w = 1; w < n; w <<= 1, m = p) { // m=p 就是优化计数排序值域for (p = 0, i = n; i > n - w; --i) id[++p] = i;for (i = 1; i <= n; ++i)if (sa[i] > w) id[++p] = sa[i] - w;memset(cnt, 0, sizeof(cnt));for (i = 1; i <= n; ++i) ++cnt[px[i] = rk[id[i]]];for (i = 1; i <= m; ++i) cnt[i] += cnt[i - 1];for (i = n; i >= 1; --i) sa[cnt[px[i]]--] = id[i];memcpy(oldrk, rk, sizeof(rk));for (p = 0, i = 1; i <= n; ++i)rk[sa[i]] = cmp(sa[i], sa[i - 1], w) ? p : ++p;}for (i = 1; i <= n; ++i) printf("%d ", sa[i]);return 0;
}
manacher
#include <bits/stdc++.h>
using namespace std;
const int N = 22000010;
char s[N];
char str[N];
int p[N];//表示以i为中心的最长回文子串长度,int init() {int len = strlen(s);str[0] = '@', str[1] = '#';int j = 2;for (int i = 0; i < len; ++i) str[j++] = s[i], str[j++] = '#';str[j] = '\0';
// cout<<j<<endl;return j;
}
int manacher() {int ans = -1, len = init(); int mx = 0;//同时记录一个当前延伸最远的回文子串 int id = 0;//对称中心 for (int i = 1; i < len; ++i) {if (i < mx) p[i] = min(p[id * 2 - i], mx - i);else p[i] = 1;while (str[i + p[i]] == str[i - p[i]]) p[i]++;if (p[i] + i > mx) mx = p[i] + i, id = i;ans = max(ans, p[i] - 1);}return ans;
}
int main()
{while(cin>>s){cout << manacher()<<endl;char ch=getchar();}return 0;
}
hash
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
ull base=131;
ull a[10010];
char s[10010];
int n,ans=1;
ull hashs(char s[])
{int len=strlen(s);ull ans=0;for(int i=0;i<len;i++)ans+=ans*base+(ull)s[i];return ans&0x7fffffff;
}
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%s",s);a[i]=hashs(s); cout<<"a[]="<<a[i]<<endl;}sort(a+1,a+n+1);for(int i=2;i<=n;i++){if(a[i]!=a[i-1])ans++;}printf("%d\n",ans);
}
KMP
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std ;
const int maxn = 1e6+5;
int Next[maxn];
char str[maxn];
char mo[maxn];
int n1,n2;
void GetNext()
{int i=0,j=-1;while(i<n2){if(j==-1||mo[i]==mo[j]) {++i,++j,Next[i]=j;}else j=Next[j];}return ;
}
int kmp()
{int cnt=0;int i=0,j=0;int w;while(i<n1){if(j==-1||str[i]==mo[j]) i++,j++;else j=Next[j];if(j==n2){w=i-n2+1;return w;// cnt++;// j=Next[j];//完成一次匹配,将j移动到最长的前缀处,省略不必要的查找}}return -1;
}
int main()
{int t;scanf("%d",&t);while(t--){scanf("%s%s",str,mo);//str是主串 mo是副串 n1=strlen(str);n2=strlen(mo);Next[0]=-1;GetNext();printf("%d\n",kmp());}return 0;
}
最大最小表达法
#include<bits/stdc++.h>
using namespace std;
#define N 10005
char str[N];
int Minrp(char *s,int l){int i=0,j=1,k=0,t;//表示从i开始k长度和从j开始k长度的字符串相同//t用来计算相对应位置上那个字典序较大while(i<l && j<l && k<l){int mi,mj;mi=i+k;mj=j+k;if(mi>=l)mi-=l;if(mj>=l)mj-=l;// (i+k) >= l ? i+k-l : i+k// (j+k) >= l ? j+k-l : j+kt = s[mi] - s[mj];if(t==0)k++;//t为零,两数相同 else {if(t>0)i = i+k+1;//>是最小表达法 <是最大表达法 else j = j+k+1;if(i==j)j++;k = 0;}}return i;
}
int main(){int n;int T;scanf("%d",&T);while(T--){scanf("%s",str);printf("%d\n",Minrp(str,strlen(str))+1);}return 0;
}
数论
约瑟夫环
//从第一个开始报数,数到k的将被杀掉
int josepus(int n,int k)
{int r=0;for(int i=2;i<=n;i++) r=(r+k)%i;return r+1; //0~n-1 所以最后结果+1
}
//第m个被杀掉的人
int josepus(int n,int m,int k)
{r=(k-1)%(n-m+1);for(int i=2;i<=m;i++){r=(r+k)%(n-m+i);}return r+1;
}
欧拉函数
线性筛求欧拉函数
//线性筛
/* * @Author: lzyws739307453 * @Language: C++ */
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 5;
bool vis[MAXN];
int prime[MAXN], phi[MAXN];
void Euler(int n) {phi[1] = 1;int cnt = 0;for (int i = 2; i <= n; i++) {if (!vis[i]) {prime[cnt++] = i;phi[i] = i - 1;}for (int j = 0; j < cnt && prime[j] <= n / i; j++) {vis[prime[j] * i] = true;if (i % prime[j])phi[i * prime[j]] = phi[i] * (prime[j] - 1);else {phi[i * prime[j]] = phi[i] * prime[j];break;}}}
}
int main() {int n;scanf("%d", &n);Euler(n);long long ans = 0;for (int i = 1; i <= n; i++)ans += phi[i];printf("%lld\n", ans);return 0;
}
莫比乌斯反演
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;const int N=1e5+5;
int p[N+10],check[N+10],tot;
int mu[N],sum[N];
int T,n,m,d,ans;void init(){memset(check,1,sizeof check);mu[1]=1;for(int i=2;i<=N;i++){if (check[i]){p[++tot]=i;mu[i]=-1;}for (int j=1;j<=tot && p[j]*i<=N;j++){check[i*p[j]]=0;if (i%p[j]==0){mu[i*p[j]]=0;break; }else mu[i*p[j]]=-mu[i];}}for(int i=1;i<=N;i++)sum[i]=mu[i]+sum[i-1]; //维护前缀和
}int calc(int n,int m){ //求[1,n][1,m]区间内互质的(x,y)的对数int ret=0;if (n>m) swap(n,m);for (int L=1,R=0;L<=n;L=R+1){R=min(n/(n/L),m/(m/L)); // 分段ret+=(sum[R]-sum[L-1])*(n/L)*(m/L);}return ret;
}int main(){init();scanf("%d",&T);while (T--){scanf("%d%d%d",&n,&m,&d);ans=calc(n/d,m/d);printf("%d\n",ans);}return 0;
}
逆序对
归并排序求逆序对
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<cmath>
#define ll long long
#define DB double
#define inf 214748360000
#define mod 1000000007
using namespace std;
const int N=1e5+90;
int n,a[N*60],b[N*60];
ll ans;//ans为逆序对个数
void merge_sort(int l,int r)
{if(l==r) return;int mid=(l+r)>>1,i,j,k;i=l; j=mid+1; k=l;merge_sort(l,mid);merge_sort(mid+1,r);while(i<=mid && j<=r){if(a[i]<=a[j]) b[k++]=a[i++];else ans+=mid-i+1,b[k++]=a[j++]; }while(i<=mid) b[k++]=a[i++];while(j<=r) b[k++]=a[j++];//当左右子序列填完后,剩余的自动后补 memcpy(a,b+1,sizeof(b));//for(int z=l;z<=r;++z) a[z]=b[z];
}
int main()
{scanf("%d",&n);for(int i=1;i<=n;++i) scanf("%d",&a[i]);merge_sort(1,n);for(int i=1;i<=n;++i) printf("%d ",a[i]);cout<<endl;printf("%lld",ans);return 0;
}
素数
线性筛
#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define MAXL 1299710
int prime[MAXN];
int tag[MAXL];
int tot = 0;
void Prime(int N){memset(tag,0,sizeof(tag));int cnt=0;tag[0]=tag[1]=1;for(int i = 2; i<N; i++){if(!tag[i]) prime[cnt++]=i;for(int j=0;j<cnt && prime[j]*i<N; j++){tag[i*prime[j]] = 1;//prime[j]是素数,它的倍数也是素数 if(i % prime[j]==0)break;//i是某个素数的倍数,直接跳出 }}
}
int main()
{int n; Prime(99);for(int i=1;i;i++){scanf("%d",&n);if(tag[n])cout<<"不是素数"<<endl;else cout<<"是素数"<<endl;}return 0;
}
欧几里得与扩展欧几里得
欧几里得算法:
ll gcd(ll a,ll b)
{return b==0?a:gcd(b,a%b);
}
扩展欧几里得算法:
#include<iostream>
#include<cstdio>
#include<cmath>using namespace std;int exgcd(int a,int b,int &x,int &y)//扩展欧几里得算法
{if(b==0){x=1;y=0;return a; //到达递归边界开始向上一层返回}int gcd=exgcd(b,a%b,x,y);int y1=y; //把x y变成上一层的int x1=x;y=x1-(a/b)*y1;x=y1;return gcd; //得到a b的最大公因数
}
逆元
扩展欧几里得
ll exgcd(ll a,ll b,ll &x,ll &y)//扩展欧几里得算法
{if(b==0){x=1;y=0;return a; //到达递归边界开始向上一层返回}ll gcd=exgcd(b,a%b,x,y);ll y1=y; //把x y变成上一层的ll x1=x;y=x1-(a/b)*y1;x=y1;return gcd; //得到a b的最大公因数
}
ll inv(ll a,ll mod){ll x,y;ll gcd=exgcd(a,mod,x,y);if(gcd!=1)return -1;else return (x+mod)%mod;
}
费马小定理+欧拉定理
ll poww(ll a,ll b,ll mod){ll ans=1;ll base=a;while(b){if(b&1)ans=ans*base%mod;base=base*base%mod;b>>=1;}return ans%mod;
}
ll inv(ll a,ll mod){return poww(a,mod-2,mod);
}
递推求逆元
long long inv[maxn];
void init(long long n,long long p)
{
inv[1]=1;
for(int i=2;i<=n;i++)
{
inv[i]=((p-p/i)*inv[p%i]%p);
}
}
__int128高精度运算
使用__int128可以实现高精度运算,但是这种大整数无法使用函数printf输出结果,所以需要手写输出
#include <bits/stdc++.h>
using namespace std;
inline __int128 read()
{__int128 x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;
}inline void write(__int128 x)
{if(x<0){putchar('-');x=-x;}if(x>9)write(x/10);putchar(x%10+'0');
}int main()
{__int128 a = read();__int128 b = read();write(a + b);return 0;
}
唯一分解定理
博客讲解
一个数n肯定能被分解成 n=p1a1 * p2a2 . . .*pnan
prime_fac[cnt]存放的底数,都是质数
prime_index[cnt]存放的指数,也就是每个对数对应的指数
int prime_fac[N],cnt=0,sum;
int prime_index[N];
void fact(int n){for(int i=2;i*i<=n;i++){if(n%i==0){cnt++;prime_fac[cnt]=i;prime_index[cnt]=0;}while(n%i==0){n/=i;prime_index[cnt]++;sum++;}}if(n!=1){cnt++;prime_fac[cnt]=n;//底数 prime_index[cnt]=1;//指数sum++;}
}
数据结构
树链剖分
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#define Rint register int
#define mem(a,b) memset(a,(b),sizeof(a))
#define Temp template<typename T>
using namespace std;
typedef long long LL;
Temp inline void read(T &x){x=0;T w=1,ch=getchar();while(!isdigit(ch)&&ch!='-')ch=getchar();if(ch=='-')w=-1,ch=getchar();while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();x=x*w;
}#define mid ((l+r)>>1)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define len (r-l+1)const int maxn=200000+10;
int n,m,r,mod;
//见题意
int e,beg[maxn],nex[maxn],to[maxn],w[maxn],wt[maxn];
//链式前向星数组,w[]、wt[]初始点权数组
int a[maxn<<2],laz[maxn<<2];
//线段树数组、lazy操作
int son[maxn],id[maxn],fa[maxn],cnt,dep[maxn],siz[maxn],top[maxn];
//son[]重儿子编号,id[]新编号,fa[]父亲节点,
//cnt dfs_clock/dfs序,dep[]深度,siz[]子树大小,top[]当前链顶端节点
int res=0;
//查询答案 inline void add(int x,int y){//链式前向星加边 to[++e]=y;nex[e]=beg[x];beg[x]=e;
}
//-------------------------------------- 以下为线段树
inline void pushdown(int rt,int lenn){laz[rt<<1]+=laz[rt];laz[rt<<1|1]+=laz[rt];a[rt<<1]+=laz[rt]*(lenn-(lenn>>1));a[rt<<1|1]+=laz[rt]*(lenn>>1);a[rt<<1]%=mod;a[rt<<1|1]%=mod;laz[rt]=0;
}inline void build(int rt,int l,int r){if(l==r){a[rt]=wt[l];if(a[rt]>mod)a[rt]%=mod;return;}build(lson);build(rson);a[rt]=(a[rt<<1]+a[rt<<1|1])%mod;
}inline void query(int rt,int l,int r,int L,int R){if(L<=l&&r<=R){res+=a[rt];res%=mod;return;}else{if(laz[rt])pushdown(rt,len);if(L<=mid)query(lson,L,R);if(R>mid)query(rson,L,R);}
}inline void update(int rt,int l,int r,int L,int R,int k){if(L<=l&&r<=R){laz[rt]+=k;a[rt]+=k*len;}else{if(laz[rt])pushdown(rt,len);if(L<=mid)update(lson,L,R,k);if(R>mid)update(rson,L,R,k);a[rt]=(a[rt<<1]+a[rt<<1|1])%mod;}
}
//---------------------------------以上为线段树
inline int qRange(int x,int y){int ans=0;while(top[x]!=top[y]){//当两个点不在同一条链上 if(dep[top[x]]<dep[top[y]])swap(x,y);//把x点改为所在链顶端的深度更深的那个点res=0;query(1,1,n,id[top[x]],id[x]);//ans加上x点到x所在链顶端 这一段区间的点权和ans+=res;ans%=mod;//按题意取模 x=fa[top[x]];//把x跳到x所在链顶端的那个点的上面一个点}//直到两个点处于一条链上if(dep[x]>dep[y])swap(x,y);//把x点深度更深的那个点res=0;query(1,1,n,id[x],id[y]);//这时再加上此时两个点的区间和即可ans+=res;return ans%mod;
}inline void updRange(int x,int y,int k){//同上 k%=mod;while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);update(1,1,n,id[top[x]],id[x],k);x=fa[top[x]];}if(dep[x]>dep[y])swap(x,y);update(1,1,n,id[x],id[y],k);
}inline int qSon(int x){res=0;query(1,1,n,id[x],id[x]+siz[x]-1);//子树区间右端点为id[x]+siz[x]-1 return res;
}inline void updSon(int x,int k){//同上 update(1,1,n,id[x],id[x]+siz[x]-1,k);
}inline void dfs1(int x,int f,int deep){//x当前节点,f父亲,deep深度 dep[x]=deep;//标记每个点的深度 fa[x]=f;//标记每个点的父亲 siz[x]=1;//标记每个非叶子节点的子树大小 int maxson=-1;//记录重儿子的儿子数 for(Rint i=beg[x];i;i=nex[i]){int y=to[i];if(y==f)continue;//若为父亲则continue dfs1(y,x,deep+1);//dfs其儿子 siz[x]+=siz[y];//把它的儿子数加到它身上 if(siz[y]>maxson)son[x]=y,maxson=siz[y];//标记每个非叶子节点的重儿子编号 }
}inline void dfs2(int x,int topf){//x当前节点,topf当前链的最顶端的节点 id[x]=++cnt;//标记每个点的新编号 wt[cnt]=w[x];//把每个点的初始值赋到新编号上来 top[x]=topf;//这个点所在链的顶端 if(!son[x])return;//如果没有儿子则返回 dfs2(son[x],topf);//按先处理重儿子,再处理轻儿子的顺序递归处理 for(Rint i=beg[x];i;i=nex[i]){int y=to[i];if(y==fa[x]||y==son[x])continue;dfs2(y,y);//对于每一个轻儿子都有一条从它自己开始的链 }
}int main(){read(n);read(m);read(r);read(mod);for(Rint i=1;i<=n;i++)read(w[i]);for(Rint i=1;i<n;i++){int a,b;read(a);read(b);add(a,b);add(b,a);}dfs1(r,0,1);dfs2(r,r);build(1,1,n);while(m--){int k,x,y,z;read(k);if(k==1){read(x);read(y);read(z);updRange(x,y,z);//将树从 x 到 y 结点最短路径上所有节点的值都加上 z}else if(k==2){//求树从 x 到 y 结点最短路径上所有节点的值之和。 read(x);read(y);printf("%d\n",qRange(x,y));}else if(k==3){//以 x 为根节点的子树内所有节点值都加上 z。 read(x);read(y);updSon(x,y);}else{read(x);printf("%d\n",qSon(x));//求以 xx 为根节点的子树内所有节点值之和 }}
}
字典树
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int MAX_NODE = 1000000 + 10;
const int CHARSET = 26;
int trie[MAX_NODE][CHARSET] = {0};
int color[MAX_NODE] = {0};
int k = 1;void insert(char *w){int len = strlen(w);int p = 0;for(int i=0; i<len; i++){int c = w[i] - 'a';if(!trie[p][c]){trie[p][c] = k;k++;}p = trie[p][c];}color[p] = 1;
}int search(char *s){int len = strlen(s);int p = 0;for(int i=0; i<len; i++){int c = s[i] - 'a';if(!trie[p][c]) return 0;p = trie[p][c];}return color[p] == 1;
}int main(){int t,q;char s[20];scanf("%d%d", &t,&q);while(t--){scanf("%s", s);insert(s);}while(q--){scanf("%s", s);if(search(s)) printf("YES\n");else printf("NO\n");}return 0;
}
树状数组
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <string>
#include <vector>
#define For(a,b) for(int a=0;a<b;a++)
#define mem(a,b) memset(a,b,sizeof(a))
#define _mem(a,b) memset(a,0,(b+1)<<2)
#define lowbit(a) ((a)&-(a))
using namespace std;
typedef long long ll;
const int maxn = 5*1e4+5;
const int INF = 0x3f3f3f3f;
int c[maxn];
void update(int x,int y,int n){for(int i=x;i<=n;i+=lowbit(i))c[i] += y;
}
int getsum(int x){int ans = 0;for(int i=x;i;i-=lowbit(i))ans += c[i];return ans;
}
int main()
{int t;int n;int x,y,z;string s;cin >> t ;for(int j=1;j<=t;j++){scanf("%d",&n);_mem(c,n);//初始化数组中前n+1个数为0for(int i=1;i<=n;i++){scanf("%d",&z);update(i,z,n);}cout <<"Case "<<j<<":"<<endl;while(1){cin >> s;if(s[0] == 'E')break;scanf("%d%d",&x,&y);if(s[0] == 'Q')cout << getsum(y)-getsum(x-1)<<endl;else if(s[0] == 'A')update(x,y,n);elseupdate(x,-y,n);}}return 0;
}
二维树状数组
单点修改,区域求值
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+6;
int c[maxn][maxn];
int a[maxn][maxn];
int n,m;
int lowbit(int x)
{return x&(-x);
}
void add(int x,int y,int z)
{for(int i=x;i<=n;i+=lowbit(i)){for(int j=y;j<=n;j+=lowbit(j)){c[i][j]+=z;}}
}
int ask(int x,int y)
{int ans=0;for(int i=x;i;i-=lowbit(i)){for(int j=y;j;j-=lowbit(j)){ans+=c[i][j];}}return ans;
}
int main()
{cin>>n>>m;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){cin>>a[i][j];add(i,j,a[i][j]);}}while(m--){int f;cin>>f;if(f==1){int x1,y1;cin>>x1>>y1;if(a[x1][y1])add(x1,y1,-1);else add(x1,y1,1);a[x1][y1]^=1;}else {int x1,y1,x2,y2;cin>>x1>>y1>>x2>>y2;int ans=ask(x2,y2)-ask(x1-1,y2)-ask(x2,y1-1)+ask(x1-1,y1-1);cout<<ans<<endl;}}return 0;
}
区域修改,单点求值
const int MAX = 1010;
int n, cnt[MAX][MAX];int lowbit(int x){return x & (-x);
}
void add(int x, int y, int val){for(int i = x; i <= n; i += lowbit(i))for(int j = y; j <= n; j += lowbit(j))cnt[i][j] += val;
}
int sum(int x, int y){int res = 0;for(int i = x; i > 0; i -= lowbit(i))for(int j = y; j > 0; j -= lowbit(j))res += cnt[i][j];return res;
}int main()
{int t, m, x1, y1, x2, y2;char op[10];cin >> t;while(t--){scanf("%d%d", &n, &m);memset(cnt, 0, sizeof(cnt));while(m--){scanf("%s%d%d", op, &x1, &y1);if(op[0] == 'C'){scanf("%d%d", &x2, &y2);add(x1, y1, 1);add(x1, y2+1, 1);add(x2+1, y1, 1);add(x2+1, y2+1, 1);}else{printf("%d\n", sum(x1,y1) % 2);}}printf("\n");}
}
int sum(int x,int y)
{int ret = 0;int i,j;for(i = x;i>=1;i-=lowbit(i)){for(j = y;j>=1;j-=lowbit(j)){ret+=c[i][j];}}return ret;
}void add(int x,int y)
{int i,j;for(i = x;i<=n;i+=lowbit(i)){for(j = y;j<=n;j+=lowbit(j)){c[i][j]++;}}
}int main()
{int i,j,x,y,ans,t;int x1,x2,y1,y2;char op[10];scanf("%d",&t);while(t--){scanf("%d%d",&n,&m);MEM(c,0);MEM(a,0);while(m--){scanf("%s",op);if(op[0]=='C'){scanf("%d%d%d%d",&x1,&y1,&x2,&y2);x1++,y1++,x2++,y2++;add(x2,y2);add(x1-1,y1-1);add(x2,y1-1);add(x1-1,y2);}else{scanf("%d%d",&x1,&y1);x2 = x1,y2 = y1;x1++,y1++,x2++,y2++;printf("%d\n",sum(x1,y1));}}printf("\n");}return 0;
}
线段树
区间查询,单点修改
inline int search(int i,int l,int r){if(tree[i].l>=l&&tree[i].r<=r)return tree[i].sum;if(tree[i].r<l||tree[i].l>r)return 0;int sum=0;if(tree[i*2].r>=l)sum+=search(i*2,l,r);if(tree[i*2+1].l<=r)sum+=search(i*2+1,l,r);return sum;
}
inline void add(int i,int dis,int k)
{if(tree[i].l==tree[i].r){tree[i].sum+=k;return;}if(dis<=tree[i*2].r)add(i*2,dis,k);else add(i*2+1,dis,k);tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;return ;
}
区间修改,单点查询
inline void add(int i,int l,int r,int k)
{if(tree[i].l>=l&&tree[i].r<=r){tree[i].num+=k;return;}if(tree[i*2].r>=l)add(i*2,l,r,k);if(tree[i*2+1].l<=r)add(i*2+1,l,r,k);
}
void search(int i,int dis){ans+=tree[i].num;if(dis<=tree[i*2].r)search(i*2,dis);if(dis>=tree[i*2+1].l)search(i*2+1,dis);
}
基础模板
区间求和,单点修改
#include<iostream>
#include<cstdio>
#define MAXN 1000001
#define ll long long
using namespace std;
unsigned ll n,m,a[MAXN],ans[MAXN<<2],tag[MAXN<<2];
inline ll ls(ll x)
{return x<<1;
}
inline ll rs(ll x)
{return x<<1|1;
}
void scan()
{cin>>n>>m;for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);
}
inline void push_up(ll p)
{ans[p]=ans[ls(p)]+ans[rs(p)];
}
void build(ll p,ll l,ll r)
{tag[p]=0;if(l==r){ans[p]=a[l];return ;}ll mid=(l+r)>>1;build(ls(p),l,mid);build(rs(p),mid+1,r);push_up(p);
}
inline void f(ll p,ll l,ll r,ll k)
{tag[p]=tag[p]+k;ans[p]=ans[p]+k*(r-l+1);
}
inline void push_down(ll p,ll l,ll r)
{ll 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(ll nl,ll nr,ll l,ll r,ll p,ll k)
{if(nl<=l&&r<=nr){ans[p]+=k*(r-l+1);tag[p]+=k;return ;}push_down(p,l,r);ll 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);
}
ll query(ll q_x,ll q_y,ll l,ll r,ll p)
{ll res=0;if(q_x<=l&&r<=q_y)return ans[p];ll 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;
}
int main()
{ll a1,b,c,d,e,f;scan();build(1,1,n);while(m--){scanf("%lld",&a1);switch(a1){case 1:{scanf("%lld%lld%lld",&b,&c,&d);update(b,c,1,n,1,d);break;}case 2:{scanf("%lld%lld",&e,&f);printf("%lld\n",query(e,f,1,n,1));break;}}}return 0;
}
区间求和,单点乘数,单点添加
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,a[1000005],mod;
struct node{ll sum,l,r,mu,add;
}t[1000005];
ll read(){ll x=0;char ch=getchar();while(ch<'0'||ch>'9')ch=getchar();while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();return x;
}
void build(ll p,ll l,ll r){t[p].l=l,t[p].r=r;t[p].mu=1;if(l==r){t[p].sum=a[l]%mod;return ;}ll mid=(l+r)>>1;build(p*2,l,mid);build(p*2+1,mid+1,r);t[p].sum=(t[p*2].sum+t[p*2+1].sum)%mod;
}
void spread(ll p){t[p*2].sum=(ll)(t[p].mu*t[p*2].sum+((t[p*2].r-t[p*2].l+1)*t[p].add)%mod)%mod;t[p*2+1].sum=(ll)(t[p].mu*t[p*2+1].sum+(t[p].add*(t[p*2+1].r-t[p*2+1].l+1))%mod)%mod;//add已经乘过mu啦t[p*2].mu=(ll)(t[p*2].mu*t[p].mu)%mod;t[p*2+1].mu=(ll)(t[p*2+1].mu*t[p].mu)%mod;t[p*2].add=(ll)(t[p*2].add*t[p].mu+t[p].add)%mod;t[p*2+1].add=(ll)(t[p*2+1].add*t[p].mu+t[p].add)%mod;t[p].mu=1,t[p].add=0;
}
void add(ll p,ll l,ll r,ll k){if(t[p].l>=l&&t[p].r<=r){t[p].add=(t[p].add+k)%mod;t[p].sum=(ll)(t[p].sum+k*(t[p].r-t[p].l+1))%mod;//只要加上增加的就好return ;}spread(p);t[p].sum=(t[p*2].sum+t[p*2+1].sum)%mod;ll mid=(t[p].l+t[p].r)>>1;if(l<=mid)add(p*2,l,r,k);if(mid<r)add(p*2+1,l,r,k);t[p].sum=(t[p*2].sum+t[p*2+1].sum)%mod;}
void mu(ll p,ll l,ll r,ll k){if(t[p].l>=l&&t[p].r<=r){t[p].add=(t[p].add*k)%mod;//比较重要的一步,add要在这里乘上k,因为后面可能要加其他的数而那些数其实是不用乘k的t[p].mu=(t[p].mu*k)%mod;t[p].sum=(t[p].sum*k)%mod;return ;}spread(p);t[p].sum=t[p*2].sum+t[p*2+1].sum;ll mid=(t[p].l+t[p].r)>>1;if(l<=mid)mu(p*2,l,r,k);if(mid<r)mu(p*2+1,l,r,k);t[p].sum=(t[p*2].sum+t[p*2+1].sum)%mod;
}
ll ask(ll p,ll l,ll r){if(t[p].l>=l&&t[p].r<=r){return t[p].sum;}spread(p);ll val=0;ll mid=(t[p].l+t[p].r)>>1;if(l<=mid)val=(val+ask(p*2,l,r))%mod;if(mid<r)val=(val+ask(p*2+1,l,r))%mod;return val;
}
int main(){cin>>n>>m>>mod;for(int i=1;i<=n;i++){a[i]=read();}build(1,1,n);for(int i=1;i<=m;i++){int ty=read();if(ty==1){ll cn=read(),cm=read(),cw=read();mu(1,cn,cm,cw);}else if(ty==2){ll cn=read(),cm=read(),cw=read();add(1,cn,cm,cw);}else {ll cn=read(),cm=read();cout<<ask(1,cn,cm)<<endl;}}
}
权值线段树
// luogu-judger-enable-o2
//#pragma comment(linker, "/STACK:1024000000,1024000000")#include<stdio.h>
#include<string.h>
#include<math.h>
#include<time.h>#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
using namespace std;#define ll long long
#define Pair pair<ll,int>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// 水印
//std::ios::sync_with_stdio(false);
// register
const int MAXN=2e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
const double EPS=1.0e-8;
const double PI=acos(-1.0);ll Tree[MAXN<<2];
ll a[MAXN],sum[MAXN];
ll n,t;int get_r(ll res){int l=1,r=n,mid;while(l<=r){//find the last r to sum[l-1]>sum[r]-t => res>sum[r]mid=(l+r)>>1;if(res>sum[mid]) l=mid+1;else r=mid-1;}return r;
}
int get_l(ll res){int l=1,r=n,mid;while(l<=r){mid=(l+r)>>1;if(res==sum[mid]) return mid;else if(res>sum[mid]) l=mid+1;else r=mid-1;}
}
void Update(int l,int r,int x,int oper,int rt){//修改一个点 if(l==r){Tree[rt]+=oper;return ;}int mid=(l+r)>>1;if(x<=mid) Update(l,mid,x,oper,rt<<1);else Update(mid+1,r,x,oper,rt<<1|1);Tree[rt]=Tree[rt<<1]+Tree[rt<<1|1];
}
int Query(int ql,int qr,int l,int r,int rt){if(qr==0) return 0;if(l>=ql&&r<=qr){return Tree[rt];}int mid=(l+r)>>1;int ans=0;if(ql<=mid) ans+=Query(ql,qr,l,mid,rt<<1);if(qr>mid) ans+=Query(ql,qr,mid+1,r,rt<<1|1);return ans;
}
int main(){while(~scanf("%lld%lld",&n,&t)){clean(Tree,0);for(int i=1;i<=n;++i){scanf("%lld",&a[i]);sum[i]=sum[i-1]+a[i];}sort(sum+1,sum+1+n);for(int i=1;i<=n;++i){sum[i]=sum[i]-t;}sort(sum+1,sum+1+n);for(int i=1;i<=n;++i){Update(1,n,i,1,1);}ll ans=0,res=0;for(int i=1;i<=n;++i){int r=get_r(res);
// cout<<r<<" : ";ans+=Query(1,r,1,n,1);
// cout<<ans<<" ";res+=a[i];int l=get_l(res-t);Update(1,n,l,-1,1);}printf("%lld\n",ans);}
}
/*
*/
主席树
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cctype>
#include<vector>
#include<map>
#include<queue>
#define rep(i, x, y) for (int i = (x); i <= (y); i ++)
#define down(i, x, y) for (int i = (x); i >= (y); i --)
#define mid (l+r)/2
#define lc o<<1
#define rc o<<1|1
#define pb push_back
#define mp make_pair
#define Pair pair<int, int>
#define F first
#define S second
#define B begin()
#define E end()
using namespace std;
typedef long long LL;
//headconst int N = 100010, LOG = 20;
int n, m, q, tot = 0;
int a[N], b[N];
int T[N], sum[N*LOG], L[N*LOG], R[N*LOG];inline int build(int l, int r)
{int rt = ++ tot;if (l < r){L[rt] = build(l, mid);R[rt] = build(mid+1, r);}return rt;
}inline int update(int pre, int l, int r, int x)
{int rt = ++ tot;L[rt] = L[pre]; R[rt] = R[pre]; sum[rt] = sum[pre] + 1; if (l < r){if (x <= mid) L[rt] = update(L[pre], l, mid, x);else R[rt] = update(R[pre], mid+1, r, x);}return rt;
}inline int query(int u, int v, int l, int r, int k)
{if (l == r) return l;int x = sum[L[v]] - sum[L[u]];if (x >= k) return query(L[u], L[v], l, mid, k);else return query(R[u], R[v], mid+1, r, k-x);
}int main()
{int Test; scanf("%d", &Test);while (Test --){tot = 0;memset(T, 0, sizeof T); memset(sum, 0, sizeof sum);memset(L, 0, sizeof L); memset(R, 0, sizeof R);scanf("%d%d", &n, &q);rep(i, 1, n) scanf("%d", &a[i]), b[i] = a[i];sort(b+1, b+1+n);m = unique(b+1, b+1+n)-b-1;T[0] = build(1, m);rep(i, 1, n){a[i] = lower_bound(b+1, b+1+m, a[i]) - b;T[i] = update(T[i-1], 1, m, a[i]);}while (q --){int x, y, z; scanf("%d%d%d", &x, &y, &z);int p = query(T[x-1], T[y], 1, m, z);printf("%d\n", b[p]);}}return 0;
}
#include <bits/stdc++.h>
#define maxn 200010
using namespace std;
int a[maxn], b[maxn], n, m, q, p, sz;
int lc[maxn << 5], rc[maxn << 5], sum[maxn << 5], rt[maxn << 5];
//空间要注意inline int read(){int s = 0, w = 1;char c = getchar();for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);return s * w;
}void build(int &rt, int l, int r){rt = ++sz, sum[rt] = 0;if (l == r) return;int mid = (l + r) >> 1;build(lc[rt], l, mid); build(rc[rt], mid + 1, r);
}int update(int o, int l, int r){int oo = ++sz;lc[oo] = lc[o], rc[oo] = rc[o], sum[oo] = sum[o] + 1;if (l == r) return oo;int mid = (l + r) >> 1;if (mid >= p) lc[oo] = update(lc[oo], l, mid); else rc[oo] = update(rc[oo], mid + 1, r);return oo;
}int query(int u, int v, int l, int r, int k){int mid = (l + r) >> 1, x = sum[lc[v]] - sum[lc[u]];if (l == r) return l;if (x >= k) return query(lc[u], lc[v], l, mid, k); else return query(rc[u], rc[v], mid + 1, r, k - x);
}int main(){n = read(), m = read();for (int i = 1; i <= n; ++i) a[i] = read(), b[i] = a[i];sort(b + 1, b + 1 + n);q = unique(b + 1, b + 1 + n) - b - 1;build(rt[0], 1, q);for (int i = 1; i <= n; ++i){p = lower_bound(b + 1, b + 1 + q, a[i]) - b;rt[i] = update(rt[i - 1], 1, q);} while (m--){int l = read(), r = read(), k = read();printf("%d\n", b[query(rt[l - 1], rt[r], 1, q, k)]);}return 0;
}
单调栈
博客讲解
typedef pair<int, int>pa;int n, m;
int a[2005][2005];
pa f[2005];
stack<pa>s;int main() {while(~scanf("%d%d", &n, &m)){for(int i = 1; i <= n; i++){for(int j = 1; j <= m; j++){scanf("%d", &a[i][j]);if (a[i][j]) a[i][j] += a[i-1][j];//求连续的高度 }}int ans = 0;pa v;for(int i = 1; i <= n; i++){for(int j = 1; j <= m; j++){if (s.empty() && !a[i][j]) continue;if (s.empty() || s.top().second <= a[i][j])//当高度小于时 s.push(make_pair(j, a[i][j]));else {while(!s.empty() && s.top().second > a[i][j]){v = s.top();s.pop();int area = (j-v.first)*v.second;//求面积 if (area > ans) ans = area; }s.push(make_pair(v.first, a[i][j]));}}}printf("%d\n", ans);}return 0;
}
图论
拓扑排序
/*hdu1285--采用邻接表记录两者之间的关系*/
#include<cstdio>
#include<cstring>
int head[510];
int indegree[510];
int queue[510];
int num;
struct stu{int to,next;
}edge[2510];
void inin(){//初始化memset(indegree,0,sizeof(indegree));memset(head,-1,sizeof(head));num=0;
}
void add(int a,int b){//添加边stu E={b,head[a]};edge[num]=E;head[a]=num++;indegree[b]++;
}
void topo(int n){int i,j,id,t=0;for(j=1;j<=n;j++){for(i=1;i<=n;i++){if(indegree[i]==0){id=i;break;}}queue[t++]=id;indegree[id]=-1;for(i=head[id];i!=-1;i=edge[i].next){int k=edge[i].to;indegree[k]--;}}printf("%d",queue[0]);//输出拓扑序列for(i=1;i<n;++i){printf(" %d",queue[i]);}printf("\n");
}
int main(){int n,m,i,j,a,b;while(scanf("%d%d",&n,&m)!=EOF){inin();for(i=1;i<=m;i++){scanf("%d%d",&a,&b);add(a,b);}topo(n);}return 0;
}
tarjan
#include<iostream> //输出所有强连通分量
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;int n,m,x,y,top=0,cnt=0,t,col;
int ans1=-1,ans2=-1,ans3=-1;
int d[200020];
int a[200020];
int c[200020];
int f[200020];
int dfn[200020];
int low[200020];
int stack[200020];bool v[200020];struct edge{int u;int v;int w;int next;
}e[1000020];void Add(int u,int v,int w)
{++top;e[top].u=u;e[top].v=v;e[top].w=w;e[top].next=f[u];f[u]=top;
}int read()
{int x=0;int k=1;char c=getchar();while(c>'9'||c<'0'){if(c=='-') k=-1;c=getchar();}while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return x*k;
}void tarjan(int now)
{dfn[now]=low[now]=++cnt;//初始化stack[++t]=now;//入栈操作v[now]=1;//v[]代表该点是否已入栈for(int i=f[now];i!=-1;i=e[i].next)//邻接表存图if(!dfn[e[i].v]) //判断该点是否被搜索过{tarjan(e[i].v);low[now]=min(low[now],low[e[i].v]); //回溯时更新low[ ],取最小值}else if(v[e[i].v])low[now]=min(low[now],dfn[e[i].v]);
//一旦遇到已入栈的点,就将该点作为连通量的根
//这里用dfn[e[i].v]更新的原因是:这个点可能
//已经在另一个强连通分量中了但暂时尚未出栈,所//以now不一定能到达low[e[i].v]但一定能到达//dfn[e[i].v].if(dfn[now]==low[now]){int cur;do{cur=stack[t--];v[cur]=false; //不要忘记出栈}while(now!=cur);printf("now=%d\n",now);}
}
int main()
{n=read();m=read();memset(f,-1,sizeof f);for(int i=1;i<=n;++i)a[i]=read();for(int i=1;i<=m;++i){x=read();y=read();Add(x,y,0);}for(int i=1;i<=n;++i)if(!dfn[i]) tarjan(i);return 0;
}
求割点
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
using namespace std;
const int MAXN = 20000+10;
int N, M;
vector <int> E[MAXN], cutp;
int dfn[MAXN], low[MAXN], timer;
void dfs(int u, int fa){dfn[u] = low[u] = ++timer;int son = 0;bool f = false;for(int i=0;i<E[u].size();i++){int v = E[u][i];if(v == fa) continue;if(!dfn[v]){son++;dfs(v, u);if(dfn[u] <= low[v]) f = true;low[u] = min(low[u], low[v]);}else low[u] = min(low[u], dfn[v]);}if((!fa && son>1) || (fa && f)) cutp.push_back(u);
}
void Tarjan(){for(int i=1;i<=N;i++){if(!dfn[i]) dfs(i, 0);}
}
int main(){scanf("%d%d", &N, &M);while(M--){int from, to;scanf("%d%d", &from, &to);E[from].push_back(to);E[to].push_back(from);}Tarjan();printf("%d\n", (int)cutp.size());sort(cutp.begin(), cutp.end());for(int i=0;i<cutp.size();i++)printf(i<cutp.size()-1?"%d ":"%d\n", cutp[i]);return 0;
}
二分图匹配–匈牙利算法
#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 3;
int n = maxn, m = maxn;
int Map[maxn][maxn];//map[i][j]=1表示X部的i和Y部的j存在路径,是否可以匹配
int cx[maxn], cy[maxn];
bool vis[maxn];
//cx[i]表示X部i点匹配的Y部顶点的编号
//cy[i]表示Y部i点匹配的X部顶点的编号bool dfs(int u)//dfs进入的都是X部的点
{for (int v = 0; v < n; v++)//枚举Y部的点,判断X部的u和Y部的v是否存在路径{//如果存在路径并且还没被标记加入增广路if (Map[u][v] && !vis[v])//vis数组只标记Y组{//标记加入增广路vis[v] = 1;//如果Y部的点v还未被匹配//或者已经被匹配了,但是可以从v点原来匹配的cy[v]找到一条增广路//说明这条路就可是一个正确的匹配//因为递归第一次进入dfs时,u是未匹配的//如果v还没有匹配对象,即和它相连的所有边都不在,已经选择的匹配边集合M(M\in E)中,这时就找到了u-v增广路径//如果v已经有匹配对象了,那么u-v是一条未选择的边,//而v-cy[v] \in M 则是一条已经选择的边, dfs(cy[v])从cy[v]开始搜索增广路径//如果新的v'没有匹配对象,那么u-v-cy[v]-v'就是一条增广路径,//如果v'已经有匹配对象了,那么根据匹配是唯一的,//cy[v]-v'一定不在已经选择的边中(和cy[v]-v冲突),//u-v-cy[v]-v'-cy[v']符合增广路径对边顺序的要求,继续利用dfs(cy[v'])搜索u-v-cy[v]-v'-cy[v']-下面的点//当搜索到增广链时,如u-v-cy[v]-v',那么经过递归的匹配调整和return 1,进行匹配增广操作,假设dfs0 是main调用的dfs算法,dfs1是dfs0调用的dfs算法//在dfs1中进行cy[v]-v'的匹配,因为dfs1返回1,因此在dfs0中进行u-v的匹配,匹配增广操作的结果是{cy[v]-v}->{u-v,cy[v]-v'}//如果在一个dfs(k)自调用的dfs(k+1)中,遍历所有的v(k+1),要么已经有匹配点了,要么和输入u(k+1)没有连接可能,这时搜索终止,说明不存在经过u(k+1)的增广链,返回0//而在main调用的dfs(0)中,调用的dfs(1)返回的都是0,而且v都是已经有匹配了,那么不存在从该点出发的增广链,那么就该点就不在最大匹配当中//为什么找不到增广链就不在最大匹配当中呢?感觉可以用反证法证明,博客中下面内容可能有更新这方面的思考if (cy[v] == -1 || dfs(cy[v])){cx[u] = v;//可以匹配,进行匹配cy[v] = u;return 1;}}}return 0;//不能匹配
}
int maxmatch()//匈牙利算法主函数
{int ans = 0;//匹配清空,全部置为-1memset(cx, -1, sizeof(cx));memset(cy, -1, sizeof(cy));for (int i = 0; i < n; i++){if (cx[i] == -1)//如果X部的i还未匹配{memset(vis, 0, sizeof(vis));//每次找增广路的时候清空visans += dfs(i);}}return ans;
}int main()
{//输入匹配的两个点集合的数量cin >> n >> m;//输入两个点集合成员间的匹配可能int x, y;for (int i = 0; i < m; i++){cin >> x >> y;Map[x][y] = 1;}//执行匈牙利算法,输出最大匹配cout << maxmatch() << endl;return 0;
}
最优匹配
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 300 + 10;
int sx[maxn], sy[maxn], vx[maxn], vy[maxn], match[maxn];
int money[maxn][maxn], slack[maxn];
int n;
void chushihua()
{memset(sy, 0, sizeof(sy));for(int i = 1; i <= n; i++){sx[i] = 1;for(int j = 1; j <= n; j++){scanf("%d", &money[i][j]);sx[i] = max(sx[i], money[i][j]);}}
}
bool dfs(int x)
{vx[x] = true;for(int i = 1; i <= n; i++){int xx= sx[x] + sy[i] - money[x][i];if(!vy[i] && !xx){vy[i] = true;if(match[i]==-1 || dfs(match[i])){match[i] = x;return true;}}else if(slack[i] > xx)slack[i] = xx;}return false;
}
void bestmatch()
{for(int i = 1; i <= n; i++){while(1){memset(vx, false, sizeof(vx));memset(vy, false, sizeof(vy));memset(slack, 0x3f, sizeof(slack));if(dfs(i))break;int dd = inf;for(int i = 1; i <= n; i++)if(!vy[i] && dd>slack[i])dd = slack[i];for(int i = 1; i <= n; i++){if(vx[i])sx[i] -= dd;if(vy[i])sy[i] += dd;}}}
}
int main()
{while(~scanf("%d", &n)){memset(match, -1, sizeof(match));chushihua();bestmatch();int sum = 0;for(int i = 1; i <= n; i++)sum += money[match[i]][i];printf("%d\n", sum);}return 0;
}
网络流问题
Dinic
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define maxn 1000001
#define INF 19260817
using namespace std;
int cnt,cost[maxn],from[maxn],to[maxn],Next[maxn],head[maxn];
int level[maxn];
queue<int>q;
int S,T,n,m;
void add(int x,int y,int z){ //建边++cnt;cost[cnt]=z;from[cnt]=x;to[cnt]=y;Next[cnt]=head[x];head[x]=cnt;
}
bool bfs(){ //bfs分层memset(level,-1,sizeof(level));level[S]=0;q.push(S);while(!q.empty()){int u=q.front();q.pop();for(int i=head[u];i!=-1;i=Next[i]){int v=to[i];if(cost[i]!=0&&level[v]==-1){ //如果容量是0||已被更新就不更新了level[v]=level[u]+1;q.push(v);}}}if(level[T]!=-1)return true; //如果流不动了就结束dinicreturn false;
}
int dfs(int u,int flow){ //dfs找最大流if(u==T)return flow;int ret=flow; //记录初始流量for(int i=head[u];i!=-1;i=Next[i]){if(ret<=0)break; //如果已经没流了就退出int v=to[i];if(cost[i]!=0&&level[u]+1==level[v]){int k=dfs(v,min(cost[i],ret)); //把能流的都给下一个点ret-=k;cost[i]-=k;cost[i^1]+=k; //边权更新,剩余流量更新}}return flow-ret; //返回流出的流量
}
int dinic(){int ans=0;while(bfs()==true){ans+=dfs(S,INF); //累加最大流}return ans;
}
int main(){cnt=1;memset(head,-1,sizeof(head));scanf("%d%d%d%d",&n,&m,&S,&T);for(int i=1;i<=m;i++){int x,y,z;scanf("%d%d%d",&x,&y,&z);add(x,y,z);add(y,x,0); //EK一样建边}printf("%d",dinic());
}
//dinic时间复杂度:O(n^2 m).
KM算法
#include<iostream>
#include<cstdio>
#include<cstring>using namespace std;const int N=310;
const int INF=0x3f3f3f3f;int n,nx,ny;
int linker[N],lx[N],ly[N],slack[N]; //lx,ly为顶标,nx,ny分别为x点集y点集的个数
int visx[N],visy[N],w[N][N];int DFS(int x){visx[x]=1;for(int y=1;y<=ny;y++){if(visy[y])continue;int tmp=lx[x]+ly[y]-w[x][y];if(tmp==0){visy[y]=1;if(linker[y]==-1 || DFS(linker[y])){linker[y]=x;return 1;}}else if(slack[y]>tmp){ //不在相等子图中slack 取最小的slack[y]=tmp;}}return 0;
}int KM(){int i,j;memset(linker,-1,sizeof(linker));memset(ly,0,sizeof(ly));for(i=1;i<=nx;i++) //lx初始化为与它关联边中最大的for(j=1,lx[i]=-INF;j<=ny;j++)if(w[i][j]>lx[i])lx[i]=w[i][j];for(int x=1;x<=nx;x++){for(i=1;i<=ny;i++)slack[i]=INF;while(1){memset(visx,0,sizeof(visx));memset(visy,0,sizeof(visy));if(DFS(x)) //若成功(找到了增广轨),则该点增广完成,进入下一个点的增广break; //若失败(没有找到增广轨),则需要改变一些点的标号,使得图中可行边的数量增加。//方法为:将所有在增广轨中(就是在增广过程中遍历到)的X方点的标号全部减去一个常数d,//所有在增广轨中的Y方点的标号全部加上一个常数dint d=INF;for(i=1;i<=ny;i++)if(!visy[i] && d>slack[i])d=slack[i];for(i=1;i<=nx;i++)if(visx[i])lx[i]-=d;for(i=1;i<=ny;i++) //修改顶标后,要把所有不在交错树中的Y顶点的slack值都减去dif(visy[i])ly[i]+=d;elseslack[i]-=d;}}int res=0;for(i=1;i<=ny;i++)if(linker[i]!=-1)res+=w[linker[i]][i];return res;
}int main(){//freopen("input.txt","r",stdin);while(~scanf("%d",&n)){nx=ny=n;for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&w[i][j]);int ans=KM();printf("%d\n",ans);}return 0;
}
最短路径问题
dijstra
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int MAXN=10010,MAXM=500010;
int inf=2147483647;
struct XY{int w,to,pre;
}e[MAXM];struct XX{int dis,num;
}d[MAXN],tmp;struct cmp1{bool operator ()(XX &a,XX &b){return a.dis>b.dis;}
};int n,m,s,sz=0;
int las[100010];
bool flag[MAXN];
priority_queue<XX,vector<XX>,cmp1> q;void add(int x,int y,int w){++sz;e[sz].to=y;e[sz].w=w;e[sz].pre=las[x];las[x]=sz;
}void Dijkstra(){int min,u=0;d[s].dis=0;q.push(d[s]);while (!q.empty()){u=q.top().num;q.pop();if (flag[u]) continue;flag[u]=true;for (int j=las[u];j;j=e[j].pre){int mu=e[j].to;if (d[mu].dis>d[u].dis+e[j].w){d[mu].dis=d[u].dis+e[j].w;q.push(d[mu]);}}}
}int main(){int xx,yy,zz;cin >>n>>m>>s;for (int i=1;i<=n;++i){d[i].num=i;d[i].dis=inf;}for (int i=1;i<=m;++i){scanf("%d%d%d",&xx,&yy,&zz);add(xx,yy,zz);}Dijkstra();for (int i=1;i<=n;++i)printf("%d ",d[i].dis);cout <<endl;return 0;
}
#include<bits/stdc++.h>
using namespace std;
#define maxn 10005
#define maxm 500005
#define INF 1234567890
inline int read()
{int x=0,k=1; char c=getchar();while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();return x*k;
}
struct Edge
{int u,v,w,next;
}e[maxm];
int head[maxn],cnt,n,m,s,vis[maxn],dis[maxn],pre[maxn];
struct node
{int w,now;inline bool operator <(const node &x)const//重载运算符把最小的元素放在堆顶(大根堆){return w>x.w;//这里注意符号要为'>'}
};
priority_queue<node>q;
//优先队列,其实这里一般使用一个pair,但为了方便理解所以用的结构体
inline void add(int u,int v,int w)
{e[++cnt].u=u;//这句话对于此题不需要,但在缩点之类的问题还是有用的e[cnt].v=v;e[cnt].w=w;e[cnt].next=head[u];//存储该点的下一条边head[u]=cnt;//更新目前该点的最后一条边(就是这一条边)
}
//链式前向星加边void print(int x)
{if(pre[x]==0)return ;print(pre[x]);cout<<"-> "<<x;
}
void dijkstra()
{for(int i=1;i<=n;i++){dis[i]=INF;}dis[s]=0;//赋初值q.push((node){0,s});while(!q.empty())//堆为空即为所有点都更新{node x=q.top();q.pop();int u=x.now;//记录堆顶(堆内最小的边)并将其弹出if(vis[u]) continue; //没有遍历过才需要遍历vis[u]=1;for(int i=head[u];i;i=e[i].next)//搜索堆顶所有连边{int v=e[i].v;if(dis[v]>dis[u]+e[i].w){dis[v]=dis[u]+e[i].w;pre[v]=u;//松弛操作q.push((node){dis[v],v});//把新遍历到的点加入堆中}}}
}
int main()
{n=read(),m=read(),s=read();for(int i=1,x,y,z;i<=m;i++){x=read(),y=read(),z=read();add(x,y,z);}dijkstra();for(int i=1;i<=n;i++){printf("%d ",dis[i]);// print(i);// cout<<endl;}return 0;
}
SPFA
void SPFA(int x)
{d[x]=0;for(int i=1;i<=n;i++)d[i]=+OO;queue<int>Q;Q.push(x);inq[x]=true;while(!Q.empty()){int k=Q.front;Q.pop();inq[k]=false;for(int i=head[k];i!=0;i=edge[i].next){int j=edge[i].u ;if(d[j]>d[k]+edge[i].w ){d[j]=d[k]+edge[i].w;if(inq[j]!){Q.push(j);inq[k]=true;} }}}
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
const long long inf=2147483647;
const int maxn=10005;
const int maxm=500005;
using namespace std;
int n,m,s,num_edge=0;
int dis[maxn],vis[maxn],head[maxm];
struct Edge
{int next,to,dis;
}edge[maxm]; //结构体表示静态邻接表
void addedge(int from,int to,int dis) //邻接表建图
{ //以下是数据结构书上的标准代码,不懂翻书看解释edge[++num_edge].next=head[from]; //链式存储下一条出边edge[num_edge].to=to; //当前节点编号edge[num_edge].dis=dis; //本条边的距离head[from]=num_edge; //记录下一次的出边情况
}
void spfa()
{queue<int> q; //spfa用队列,这里用了STL的标准队列for(int i=1; i<=n; i++) {dis[i]=inf; //带权图初始化vis[i]=0; //记录点i是否在队列中,同dijkstra算法中的visited数组}q.push(s); dis[s]=0; vis[s]=1; //第一个顶点入队,进行标记while(!q.empty()){int u=q.front(); //取出队首q.pop(); vis[u]=0; //出队标记for(int i=head[u]; i; i=edge[i].next) //邻接表遍历,不多解释了(也可用vector代替){int v=edge[i].to; if(dis[v]>dis[u]+edge[i].dis) //如果有最短路就更改{dis[v]=dis[u]+edge[i].dis;if(vis[v]==0) //未入队则入队{vis[v]=1; //标记入队q.push(v);}}}}
}
int main()
{cin>>m>>n;s=n;for(int i=1; i<=m; i++){int f,g,w;cin>>f>>g>>w; addedge(f,g,w); //建图,有向图连一次边就可以了addedge(g,f,w);}spfa(); //开始跑spfacout<<dis[1]<<endl; //否则打印最短距离return 0;
}
搜索
动态规划
计算几何
其他算法
快速输入输出
快读
inline int read(){int s=0,w=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);return s*w;
}
快输
inline void write(int x)
{if(x<0) {putchar('-');x = -x;}if(x>9) write(x / 10);putchar(x % 10 + '0');
}
C++大数
大数加法
string sum(string s1,string s2)
{if(s1.length()<s2.length()){string temp=s1;s1=s2;s2=temp;}int i,j;for(i=s1.length()-1,j=s2.length()-1;i>=0;i--,j--){s1[i]=char(s1[i]+(j>=0?s2[j]-'0':0)); //注意细节if(s1[i]-'0'>=10){s1[i]=char((s1[i]-'0')%10+'0');if(i) s1[i-1]++;else s1='1'+s1;}}return s1;
}
大数乘以整形数
string Multiply(string s,int x) //大数乘以整形数
{reverse(s.begin(),s.end());int cmp=0;for(int i=0;i<s.size();i++){cmp=(s[i]-'0')*x+cmp;s[i]=(cmp%10+'0');cmp/=10;}while(cmp){s+=(cmp%10+'0');cmp/=10;}reverse(s.begin(),s.end());return s;
}
大数除以整形数
string Except(string s,int x) //大数除以整形数
{int cmp=0,ok=0;string ans="";for(int i=0;i<s.size();i++){cmp=(cmp*10+s[i]-'0');if(cmp>=x){ok=1;ans+=(cmp/x+'0');cmp%=x;}else{if(ok==1)ans+='0'; //注意这里啊。才找出错误}}return ans;
}
大数乘法
string sum(string s1,string s2) //大数加法
{if(s1.length()<s2.length()){string temp=s1;s1=s2;s2=temp;}int i,j;for(i=s1.length()-1,j=s2.length()-1;i>=0;i--,j--){s1[i]=char(s1[i]+(j>=0?s2[j]-'0':0)); //注意细节if(s1[i]-'0'>=10){s1[i]=char((s1[i]-'0')%10+'0');if(i) s1[i-1]++;else s1='1'+s1;}}return s1;
}string Mult(string s,int x) //大数乘以整形数
{reverse(s.begin(),s.end());int cmp=0;for(int i=0;i<s.size();i++){cmp=(s[i]-'0')*x+cmp;s[i]=(cmp%10+'0');cmp/=10;}while(cmp){s+=(cmp%10+'0');cmp/=10;}reverse(s.begin(),s.end());return s;
}
string Multfa(string x,string y) //大数乘法
{string ans;for(int i=y.size()-1,j=0;i>=0;i--,j++){string tmp=Mult(x,y[i]-'0');for(int k=0;k<j;k++)tmp+='0';ans=sum(ans,tmp);}return ans;
}
Java大数
博客讲解
A == B
import java.math.BigDecimal;
import java.util.Scanner;public class Main {public static void main(String[] args) {// TODO Auto-generated method stubBigDecimal a, b;Scanner cin = new Scanner(System.in);while (cin.hasNext()) {a = cin.nextBigDecimal();b = cin.nextBigDecimal();if (a.compareTo(b) == 0) System.out.println("YES");else System.out.println("NO");}}}
大整数加法
import java.math.BigInteger;
import java.util.Scanner;public class Main {public static void main(String[] args) {// TODO Auto-generated method stubBigInteger a,b;Scanner cin = new Scanner(System.in);a = cin.nextBigInteger();b = cin.nextBigInteger();System.out.println(a.add(b));}}
大数阶乘
import java.math.BigInteger;
import java.util.Scanner;public class Main {public static void main(String[] args) {BigInteger f[] = new BigInteger[5500];f[0] = f[1] = BigInteger.ONE;for (int i = 2; i <= 5000; ++i) {f[i] = f[i - 1].multiply(BigInteger.valueOf(i));}Scanner cin = new Scanner(System.in);while (cin.hasNext()) {int m = cin.nextInt();System.out.println(f[m]);}}
}
大斐波那契数
import java.math.BigInteger;
import java.util.Scanner;public class Main {public static void main(String[] args) {// TODO Auto-generated method stubScanner cin = new Scanner(System.in);BigInteger[] nums = new BigInteger[1010];nums[1] = new BigInteger("1");nums[2] = new BigInteger("1");for(int i = 3; i <= 1000; i++)nums[i] = nums[i - 1].add(nums[i - 2]);int T = cin.nextInt();while(T > 0){T--;int n = cin.nextInt();System.out.println(nums[n]);}}}
A/B
import java.math.BigInteger;
import java.util.Scanner;public class Main {public static void main(String[] args) {// TODO Auto-generated method stubScanner cin = new Scanner(System.in);int T = cin.nextInt();while(T > 0){T--;BigInteger a = cin.nextBigInteger();BigInteger b = cin.nextBigInteger();BigInteger d = new BigInteger("9973");BigInteger z = new BigInteger("0");for(BigInteger i = new BigInteger("1"); ; i = i.add(new BigInteger("1"))){BigInteger c = b.multiply(i);if(c.mod(d).compareTo(a) == 0){System.out.println(i.mod(d));break;}}}}}
莫队算法
博客讲解
常规莫队
#include<bits/stdc++.h>
using namespace std;const int maxn=50005;
long long int c[maxn];
long long int sum[maxn];struct node{long long int l,r,num;
}q[maxn];long long anss[maxn];long long int block;
long long int ans=0;bool cmp(node a,node b)
{return (a.r/block)==(b.r/block)?a.l<b.l:a.r<b.r;}
/*
//根据奇偶性排序
bool cmp(node a,node b)
{return pos[a.l]^pos[b.l]?pos[a.l]<pos[b.l]:pos[a.l]&1?a.r<b.r:a.r>b.r;} */inline void del(int x)
{sum[c[x]]--;ans-=2*sum[c[x]]+1;
}inline void add(int x)
{sum[c[x]]++;ans+=2*sum[c[x]]-1;
}int main()
{long long int n,m,k;cin>>n>>m>>k;block=sqrt(n);for(long long i=1;i<=n;i++)cin>>c[i];for(long long i=1;i<=m;i++){cin>>q[i].l>>q[i].r;q[i].num=i;}sort(q+1,q+1+m,cmp);int l=1,r=0;for(long long i=1;i<=m;i++){long long ql=q[i].l,qr=q[i].r;while(l<ql){del(l++);//l++; }while(l>ql){// l--;add(--l);}while(r<qr){// r++;add(++r);}while(r>qr) {del(r--);// r--;}anss[q[i].num]=ans;}for(long long i=1;i<=m;i++)printf("%lld\n",anss[i]);return 0;
}
带修改莫队
int cmp(const node1 &a,const node1 &b)
{if (a.l/unit!=b.l/unit) return a.l/unit<b.l/unit;else if (a.r/unit!=b.r/unit) return a.r/unit<b.r/unit;else return a.x<b.x;
}
主函数维护答案:
void solve()
{int l=1,r=0,now=0;for (int i=1;i<=m;i++){while (r<q[i].r) update(r+1,1),r++;while (r>q[i].r) update(r,-1),r--;while (l>q[i].l) update(l-1,1),l--;while (l<q[i].l) update(l,-1),l++;while (now<q[i].x) change(now+1,1,l,r),now++;while (now>q[i].x) change(now,-1,l,r),now--;ans[q[i].id]=tot;}
}
修改通过change实现:
void change(int bh,int z,int l,int r)
{if (ch[bh].x>=l&&ch[bh].x<=r) update(ch[bh].x,-1); //删除从前的影响 swap(c[ch[bh].x],ch[bh].y); if (ch[bh].x>=l&&ch[bh].x<=r) update(ch[bh].x,1); //添加影响
}