一.对拍
新建 1.bat,然后编辑
:loop
date.exe
pro.exe
check.exe
fc pro.out check.out
if %errorlevel%==0 goto loop
二.考试须知
1.内存(小心开炸,爆零)
若使用了结构体
可在主函数中:
printf("%lf\n",sizeof(P30)/1024.0/1024);
2.两数相乘(炸int)
3.取模(题目看仔细)
4.long long(注意数据范围)
5.文件名(复制)
6.读入输出(查看读入输出文件名)
7.输出调试(一定得关掉)
三.造数据
1.造树
srand(time(NULL));
int n;
cin>>n;
for(int i=0;i<n;i++)A[i]=i+1;
for(int i=1;i<=1e6;i++){
swap(A[rand()%n],A[rand()%n]);
}
for(int i=1;i<n;i++)printf("%d %d\n",A[rand()%i],A[i]);
注意:rand()大约只有3万多
四.日常加速
1.读入挂
void Rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do res=(res<<3)+(res<<1)+(c&15);
while(c=getchar(),c>=48);
}
建议不要读负数
2.正向表
适用于图论中代替vector
int nxt[M<<1],head[M],To[M],V[M],ttaE;
void addedge(int a,int b,int c){
nxt[++ttaE]=a;head[a]=ttaE;
To[tta]=b;V[tta]=c;
}
3.手打堆
五.图论
1.迪杰斯特拉
如果加上手打堆会超快
struct node{
int v,id;
bool operator<(const node &s)const{
return v>s.v;
}
};
priority_queue<node>q;
void dij(int x){
memset(dis,0,sizeof(dis));
dis[x]=0;
q.push((node){0,x});
while(!q.empty()){
node now=q.top();q.pop();
if(dis[now.id]<now.v)continue;
LFOR(i,x){
int y=To[i];
int v=V[i];
if(dis[y]==-1||dis[y]>dis[x]+v){
dis[y]=dis[x]+v;
q.push((node){dis[y],y});
}
}
}
}
2.SPFA
实现较为轻松,一般情况跑得较快,但复杂度玄学,可能被卡成n*m
void spfa(int x){
memset(dis,-1,sizeof(dis));
memset(mark,0,sizeof mark);
mark[x]=1;
queue<int>q;
q.push(x);
while(!q.empty()){
int now=q.front();q.pop();
mark[now]=0;
LFOR(i,x){
int nxt=To[i];
if(dis[nxt]>dis[now]+V[i]||dis[nxt]==-1){
dis[nxt]=dis[now]+V[i];
if(!mark[nxt]){
q.push(nxt);
mark[nxt]=1;
}
}
}
}
}
3.Bell_man
可判负环
void relax(int u,int v,int w){
if(dis[u]>dis[v]+w)dis[u]=dos[v]+w;
}
void bellman(){
for(int i=1;i<n;i++){
for(int j=1;j<=m;j++){
relax(edge[j].x,edge[j].y,edge[j].v);
}
}
//判负环
bool flag=0;
for(int i=1;i<=m;i++){
if(dis[edge[i].x]>dis[edge[i].y]+edge[i].v){flag=1;break;}
}
return flag;
}
4.floyd
水分利器,再次强调三层for的顺序
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
chk_mi(dis[i][j],dis[i][k]+dis[k][j]);
5.最小生成树
这里只介绍kruskal算法 因为它快而且简单啊
struct EDGE{
int v,x,y;
bool operator <(const EDGE &_){
return v<_.v;
}
}edge[M];
int fa[M];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int kruskal{
int ans=0;
sort(edge+1,edge+m+1);
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++){
int a=find(edge[i].x),b=find(edge[i].y);
if(a!=b){
ans+=edge[i].v;
fa[a]=b;
}
}
return ans;
}
6.LCA(倍增)
实现容易,比树链剖分慢一些
void dfs(int x,int f){
fa[0][x]=f;dep[x]=dep[f]+1;
LFOR(i,x){
int y=To[i];
if(y==f)continue;
dfs(y,x);
}
}
void init(){
for(int i=1;i<S;i++)
for(int j=1;j<=n;j++)
fa[i][x]=fa[i-1][fa[i-1][x]];
}
void Up(int &x,int len){
for(int i=0;i<S;i++)if(len&(1<<i))x=fa[i][x];
}
int LCA(int x,int y){
if(dep[x]>dep[y])swap(x,y);
Up(y,dep[y]-dep[x]);
if(x==y)return x;
for(int i=S-1;i>=0;i--){
if(fa[i][x]!=fa[i][y]){
x=fa[i][x];
y=fa[i][y];
}
}
return fa[0][x];
}
7.LCA(树链剖分)
听说均摊复杂度比tarjan【O(1)】还快 orz;
#include<bits/stdc++.h>
using namespace std;
int sz[M],son[M],fa[M],Top[M],dep[M];
void ldfs(int x,int f){
sz[x]=1,son[x]=-1;
fa[x]=f,dep[x]=dep[f]+1;
LFOR(i,x){
int y=To[i];
if(y==f)continue;
ldfs(y,x);
sz[x]+=sz[y];
if(!~son[x]||sz[son[x]]<sz[y])son[x]=y;
}
}
void rdfs(int x,int tp){
Top[x]=tp;
if(!~son[x])return;
rdfs(son[x],tp);
LFOR(i,x){
int y=To[i];
if(y==son[x]||y==fa[x])continue;
rdfs(y,y);
}
}
int LCA(int x,int y){
while(Top[x]!=Top[y]){
if(dep[Top[x]]>dep[Top[y]])x=fa[Top[x]];
else y=fa[Top[y]];
}
return dep[x]<dep[y]?x:y;
}
六.数论
1.gcd
图轮只会打模板
贪心只能过样例
dp打表找规律
数论只会gcd
void gcd(int x,int y){return !x?y:gcd(y,x%y);}
2.ex_gcd
主要套了gcd的壳
这样可以算出一组解,然后满足x=x0+kb y=y0-ka;
当满足b==0是x=0,y=1;然后 y=y-x*(a/b);
void exgcd(int a,int b,int &c,int &x,int &y){
if(!b){c=a,x=1,y=0;return;}
else exgcd(b,a%b,c,y,x);y-=x*(a/b);
}
3.快速幂
ll fast(ll x,ll n,ll P){
ll res=1;
while(n){
if(n&1)res=res*x%P;
n=n>>1;
x=x*x%P;
}
return res;
}
4.组合数
1.递推
for(int i=1;i<=n;i++){
C[i][0]=C[i][i]=1;
for(int j=1;j<i;j++)C[i][j]=C[i-1][j]+C[i-1][j-1];
}
2.逆元
O(nlogn)求以n为底的组合数
C[0]=1;
for(int i=1;i<=n;i++)C[i]=C[i-1]*(n-i+1)%P*fast(i,P-2)%P;
3.线性求逆元
复杂度为O(n)
B[1]=1;
FOR(i,2,n)B[i]=1LL*(Mod-Mod/i)*B[Mod%i]%Mod;
5.埃氏筛法
void init(){
for(int i=2;i<=n;i++){
if(mark[i])continue;
for(int j=i+i;j<=n;j+=i)mark[j]=1;
}
}
七.数据结构
1.线段树
(以区间最值为例子)
1.单点更新,区间查询
struct Segment_Tree{
#define Lson l,mid,p<<1
#define Rson mid+1,r,p<<1|1
struct node{int l,r,mx;}tree[M<<2];//a为延时标记
void up(int p){
tree[p].mx=max(tree[p<<1].mx,tree[p<<1|1].mx);
}
void build(int l,int r,int p){
tree[p].l=l,tree[p].r=r;
if(l==r){
tree[p].mx=A[l];
return;
}
int mid=(l+r)>>1;
build(Lson);build(Rson);
up(p);
}
void update(int pos,int x,int p){
if(tree[p].l==l&&tree[p].r==r){
tree[p].mx=x;
return;
}
int mid=(tree[p].l+tree[p].r)>>1;
if(mid>=pos)update(pos,x,p<<1);
else update(pos,x,p<<1|1);
up(p);
}
int query(int l,int r,int p){
if(tree[p].l==l&&tree[p].r==r){
return tree[p].mx;
}
int mid=(tree[p].l+tree[p].r)>>1;
if(mid>=r)return query(l,r,p<<1);
else if(mid<l)return query(l,r,p<<1|1);
else return max(query(Lson),query(Rson));
}
}Tree;
2.区间更新,单点查询
struct Segment_Tree{
#define Lson l,mid,p<<1
#define Rson mid+1,r,p<<1|1
struct node{int l,r,mx;}tree[M<<2];//a为延时标记
void up(int p){
tree[p].mx=max(tree[p<<1].mx,tree[p<<1|1].mx);
}
void build(int l,int r,int p){
tree[p].l=l,tree[p].r=r;
if(l==r){
tree[p].mx=A[l];
return;
}
int mid=(l+r)>>1;
build(Lson);build(Rson);
up(p);
}
void update(int l,int r,int x,int p){
if(tree[p].l==l&&tree[p].r==r){
tree[p].mx=x;
return;
}
int mid=(tree[p].l+tree[p].r)>>1;
if(mid>=r)update(l,r,x,p<<1);
else if(mid<l)update(l,r,x,p<<1|1);
else update(l,mid,x,p<<1),update(mid+1,r,x,p<<1|1);
up(p);
}
int query(int pos,int p){
if(tree[p].l==tree[p].r){
return tree[p].mx;
}
int mid=(tree[p].l+tree[p].r)>>1;
if(mid>=pos)return max(tree[p].mx,query(pos,p<<1));
else return max(tree[p].mx,query(pos,p<<1|1));
}
}Tree;
3.区间更新,区间查询
struct Segment_Tree{
#define Lson l,mid,p<<1
#define Rson mid+1,r,p<<1|1
struct node{int l,r,mx,a;}tree[M<<2];//a为延时标记
void up(int p){
tree[p].mx=max(tree[p<<1].mx,tree[p<<1|1].mx);
}
void down(int p){
if(!tree[p].a)return;
tree[p<<1].a=tree[p].a,tree[p<<1|1].a=tree[p].a;
tree[p<<1].mx=tree[p].mx,tree[p<<1|1].mx=mx;
tree[p].a=0;
}
void build(int l,int r,int p){
tree[p].l=l,tree[p].r=r,tree[p].a=0;
if(l==r){
tree[p].mx=A[l];
return;
}
int mid=(l+r)>>1;
build(Lson);build(Rson);
up(p);
}
void update(int l,int r,int x,int p){
if(tree[p].l==l&&tree[p].r==r){
tree[p].mx=x;
tree[p].a=1;
return;
}
down(p);
int mid=(tree[p].l+tree[p].r)>>1;
if(mid>=r)update(l,r,x,p<<1);
else if(mid<l)update(l,r,x,p<<1|1);
else update(l,mid,x,p<<1),update(mid+1,r,p<<1|1);
up(p);
}
int query(int l,int r,int p){
if(tree[p].l==l&&tree[p].r==r){
return tree[p].mx;
}
down(p);
int mid=(tree[p].l+tree[p].r)>>1;
if(mid>=r)return query(l,r,p<<1);
else if(mid<l)return query(l,r,p<<1|1);
else return max(query(Lson),query(Rson));
}
}Tree;
2.BIT
主题思想是前缀和
空间复杂度和时间复杂度还有代码长度都是线段树的四分之一
struct BIT{
#define lowbit(x) x&-x
int Sum[M]
void update(int x,int a){
while(x<=n){
Sum[x]+=a;
x+=lowbit(x);
}
}
int query(int x){
int res=0;
while(x){
res+=Sum[x];
x-=lowbit(x);
}
return res;
}
}Bit;
3.并查集
void init(int n){for(int i=1;i<=n;i++)fa[i]=i;}
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void unite(int x,int y){fa[find(x)]=find(y);}
bool same(int x,int y){return find(x)==find(y);}
4.高精度
已经过性能测试,可能还存在什么小bug
大部分的功 能都有
#include<bits/stdc++.h>
using namespace std;
struct Bignum{
int A[1005],len;
static const int P=10000;
Bignum(){memset(A,0,sizeof A);len=1;}
void Rd(){
char C[1005];
scanf("%s",C+1);
int n=strlen(C+1);
len=0;
for(int i=n;i>=1;i-=4){
len++;
A[len]=0;
for(int j=max(1,i-3);j<=i;j++){
A[len]=(A[len]<<3)+(A[len]<<1)+(C[j]&15);
}
}
while(len>1&&!A[len])len--;
}
Bignum operator +(const Bignum &S)const{
Bignum tmp;
tmp.len=max(S.len,len);
for(int i=1;i<=tmp.len;i++){
tmp.A[i]+=A[i]+S.A[i];
if(tmp.A[i]>=P){
tmp.A[i]-=P;
tmp.A[i+1]++;
}
}
if(tmp.A[tmp.len+1])tmp.len++;
return tmp;
}
Bignum operator *(const Bignum &S)const{
Bignum tmp;
tmp.len=S.len+len-1;
for(int i=1;i<=len;i++){
for(int j=1;j<=S.len;j++){
tmp.A[i+j-1]+=A[i]*S.A[j];
tmp.A[i+j]+=tmp.A[i+j-1]/P;
tmp.A[i+j-1]=tmp.A[i+j-1]%P;
}
}
if(tmp.A[tmp.len+1])tmp.len++;
while(!tmp.A[tmp.len]&&tmp.len>1)tmp.len--;
return tmp;
}
Bignum operator -(const Bignum &S)const{
Bignum tmp;
tmp.len=max(S.len,len);
for(int i=tmp.len;i>=1;i--){
tmp.A[i]+=A[i]-S.A[i];
if(tmp.A[i]<0){
tmp.A[i]+=P;
tmp.A[i+1]--;
}
}
while(!tmp.A[tmp.len])tmp.len--;
return tmp;
}
bool operator <(const Bignum &S)const{
if(S.len!=len)return len<S.len;
for(int i=len;i>=1;i--){
if(A[i]!=S.A[i])return A[i]<S.A[i];
}
return 0;
}
bool operator ==(const Bignum &S)const{
if(len!=S.len)return 0;
for(int i=1;i<=len;i++){
if(A[i]!=S.A[i])return 0;
}
return 1;
}
bool operator <=(const Bignum &S)const{
return *this<S||*this==S;
}
Bignum operator /(const int &p)const{
Bignum tmp=*this;
for(int i=len;i>=1;i--){
if(i>1)tmp.A[i-1]+=tmp.A[i]%p*P;
tmp.A[i]=tmp.A[i]/p;
}
while(tmp.len>1&&!tmp.A[tmp.len])tmp.len--;
return tmp;
}
Bignum operator +(const int &p)const{
Bignum tmp;
tmp=*this;
tmp.A[1]+=p;
int now=1;
while(tmp.A[now]>=P){
tmp.A[now+1]++;
tmp.A[now]-=P;
now++;
}
if(tmp.A[tmp.len+1])tmp.len++;
return tmp;
}
Bignum operator -(const int &p)const{
Bignum tmp;
tmp=*this;
tmp.A[1]-=p;
int now=1;
while(tmp.A[now]<0){
tmp.A[now+1]--;
tmp.A[now]+=P;
now++;
}
if(!tmp.A[tmp.len]&&tmp.len>1)tmp.len--;
return tmp;
}
Bignum operator /(const Bignum &S)const{
Bignum l,r,res;
if(*this<S)return res;
r=*this;
while(l<=r){
Bignum mid=(l+r)/2;
if((mid*S)<=*this){
res=mid;
l=mid+1;
}
else r=mid-1;
}
return res;
}
void print(){
printf("%d",A[len]);
for(int i=len-1;i>0;i--)printf("%04d",A[i]);puts("");
}
}a,b,c;
int main(){
a.Rd(),b.Rd();
c=a/b;
c.print();
return 0;
}
八.排序
1.归并排序
2.快速排序
3.堆排序
9.错误小结
1.与系统变量冲突
(25126)全局变量y1,y0
(25081)全局函数名hash
(24221)全局函数名begin
(24198)全局数组名rank
(23836)全局数组名next
(21994)全局数组名log2
(19969)全局数组名less
(19960)全局数组名ws
(18453)全局数组名prev
(15807)全局数组名pow
(13017)全局数组名time
(11360)全局数组名end
(9924)全局数组名index
(12213)全局数组名cos
十.小工具
Mul
两数相乘可能会炸long long,但同时要取模,可以用Mul函数
LL Mul(LL x,LL y,LL p){
x=(x%p+x)%p;
y=(x%p+y)%p;
LL res=0;
while(y){
if(y&1)res=(res+x)%p;
y>>=1;
x=(x+x)%p;
}
return res;
}
C
组合数
注意i < j的情况
LL C(LL i,LL j,LL p){
if(i<j)return 0;
return A[i]*fast(A[j],p-2,p)%p*fast(A[i-j],p-2,p)%p;
}