【复习】NOIP2017提高组-背板开始
来源:互联网 发布:java easyui 编辑:程序博客网 时间:2024/06/04 01:29
打模板或许是一个很不错的选择~
好了我们就响应号召,努力打模板吧~做一个优秀的背板先生(划掉)~
1.质因数分解
虽然水,但还是很有用的~
int m=int(sqrt(n)+0.5);for(int i=2;i<=m;i++){ if(n%i==0) { num[++cnt]=i; while(n%i==0) sum[cnt]++,n/=i; }}if(n>1) num[++cnt]=n,sum[cnt]=1;
2.线性筛质数
很有用的玩意儿,可以很快地刷出所有质数,有些题很好用,
for(int i=2;i<=n;i++){ if(!H[i]) P[++cnt]=i; for(int j=1;j<=cnt&&i*P[j]<=n;j++) H[i*P[j]]=1;}
3.线性筛欧拉函数
这个好像不是很常用,理论上也不是
phi[1]=1;for(int i=2;i<=p;i++){ if(!phi[i]) { for(int j=i;j<=p;j+=i) { if(!phi[j]) phi[j]=j; phi[j]=phi[j]/i*(i-1); } }}
4.质数判定-
超牛逼的算法,可惜并不完美,所以要多测几次……
for(int w=1;w<=S;w++){ int a=rand()%(n-1)+1; int x=pow(a,u,n); for(int j=1;j<=t;j++) { int y=x*x%n; if(y==1&&x!=1&&x!=n-1) { printf("No\n"); return 0; } x=y; } if(x!=1) { printf("No\n"); return 0; }}printf("Yes\n");
5&6.快速幂
不想吐槽,为何现在才开始搞这个,跳过跳过不讲不讲
嘴上说着不讲,身体却很诚实
int pow(int a,int k,int p){ if(!k)return 1; int q=pow(a,k/2,p); if(k&1)return q*q%p*a%p; return q*q%p;}int mul(int a,int b,int p){ int ans=0; while(b) { if(b&1)ans=(ans+a)%p; a=(a<<1)%p; b>>=1; } return ans;}
7,8&9.
卧槽这个真的不讲
.
.
.
.
.
看什么看,真的不讲……
10.中国剩余定理
很牛,但是一般不会考裸题,所以有时是一个取余合数时坑你的玩意儿
还记得某大佬的出的神题(简单的组合数学
其中
for(int i=1;i<=n;i++){ int x,y; exgcd(tot/D[i],D[i],x,y); x=(x%D[i]+D[i])%D[i]; sum=(sum+1ll*(tot/D[i])*x%tot*R[i]%tot)%tot;}printf("%d",sum);
11.卡特兰数列
不是很常用,其值就等于
h[0]=h[1]=1;for(int i=2;i<=n;i++) h[i]=h[i-1]*(4*i-2)/(i+1);printf("%lld",h[n]);
12.康托展开式
for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(A[j]<A[i]) sum+=fac[n-i];printf("%d",sum+1);
13.线性求逆元
适用于求
inv[i]=-(n/i)*inv[n%i];
14.
用到的时候很少,但是一旦用到就可以恶心你半天(因为它最爱和组合数一起来猥琐你了,有时还会有
S1[0][0]=S2[0][0]=1;for(int i=1;i<=1000;i++){ S1[i][0]=0; S2[i][0]=0; for(int j= 1;j<=i;j++) { S1[i][j]=(S1[i-1][j-1]+1ll*S1[i-1][j]*(i-1)%mod)%mod; S2[i][j]=(S2[i-1][j-1]+1ll*S2[i-1][j]*j%mod)%mod; }}
15.高精度加减乘除模
emmm,这个代码太长就不放了。原理都是知道的对吧?加法、减法、乘法只要模拟一下竖式就好了,其中加减
1.链式前向星存图
超好用的存图方式!用数组代替链表,但是功能却并没有任何减弱,不仅内存只看边的数量(这是邻接矩阵所不能的),时间复杂度常数极小(这是
void add(int a,int b,int c){ ++cnt;//边数 tar[cnt]=b;//到达节点 len[cnt]=c;//边长 nex[cnt]=fir[a];//模拟链表的指针 fir[a]=cnt;//模拟链表队尾}
2.父亲-儿子-兄弟表示法存树
还是很巧妙的存树方式,可惜由于大多数时候给出的树一般都是无根树,所以使用机会还是不多
void add(int p,int q,int r){ b[q]=s[p];//兄弟 s[p]=q;//儿子 l[q]=r;//连接自己与父亲的边长 f[q]=p;//父亲}
3.
虽然很纠结英文单词写对没有,但还是就这样吧……最好写的最短路,所以一般不考(尴尬),但是相较于其他单源点算法,能够实现的功能也多了很多(例如找出负环,多源点最短路)
for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(A[i][k]+A[k][j]<A[i][j]) A[i][j]=A[i][k]+A[k][j];
4.
由于优先队列写着方便,所以我们就直接用优先队列吧(什么?我的写法内存玄学?不存在的)
void Dijkstra(int s){ priority_queue<node>q; memset(dis,0x3f,sizeof(dis));dis[s]=0; q.push(node(s,0)); while(!q.empty()) { int p=q.top().x;q.pop(); if(vis[p]) continue; vis[p]=1; for(int i=fir[p];i;i=nex[i]) { int v=tar[i]; if(dis[p]+len[i]<dis[v]) { dis[v]=dis[p]+len[i]; q.push(node(v,dis[v])); } } }}
5.
特别好写的最短路,也是最常用的最短路,不过因为时间玄学,所以可以被特殊数据卡成狗(不似
int SPFA(int s){ memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); queue<int>q; int num=0; dis[s]=0;vis[s]=1; q.push(s); while(!q.empty()) { if(num>6*m) return inf; int x=q.front();q.pop(); for(int i=fir[x];i;i=nex[i]) { int v=tar[i]; if(dis[x]+len[i]<dis[v]) { if(!vis[v]) q.push(v); dis[v]=dis[x]+len[i]; vis[v]=1; } } num++;vis[x]=0; } return dis[n];}
6.
不要问我为什么这个算法名字那么像素数……以前以为这是
void Prime(){ priority_queue<node>q; memset(dis,0x3f,sizeof(dis));dis[1]=0; memset(vis,0,sizeof(vis)); q.push(node(1,0)); ans=0; while(!q.empty()) { int p=q.top().x;q.pop(); if(vis[p]) continue; ans+=dis[p]; vis[p]=1; for(int i=fir[p];i;i=nex[i]) { int v=tar[i]; if(len[i]<dis[v]) { dis[v]=len[i]; q.push(node(v,dis[v])); } } }}
7.
虽然
for(int i=1;i<=m;i++){ int p=grand(A[i].s),q=grand(A[i].t); if(p!=q) { f[p]=q; ans+=A[i].len; }}
8.求树的重心
与树的直径一样,重心在求解树上的问题时可以极大地方便我们。用树的重心做树形
void dfs(int r,int f){ for(int i=fir[r];i;i=nex[i]) { int v=tar[i]; if(v!=f) { dfs(v,r); wei[r]=max(wei[r],siz[v]);//子树中最大节点 siz[r]+=siz[v]; } } siz[r]++; wei[r]=max(wei[r],n-siz[r]);//与除了这颗树以外的部分节点数比较 if(wei[root]>wei[r]) root=r; }
9.求树的直径
直径在求解树上问题时很常用,因为它拥有很多神奇的性质,很多时候,你要求一些具有什么特点的路径,直径一般都是符合条件的(当然也不是绝对的)。而直径很好写,只要若干次
dfs(1,0);//以1为根dfs,最远的就是直径的一个端点r1r1=1;r2=1;for(int i=1;i<=n;i++) if(dis[i]>dis[r1]) r1=i;memset(dis,0,sizeof(dis));dfs(r1,0);//以r1为根dfs,最远的就是另一个端点r2for(int i=1;i<=n;i++) if(dis[i]>dis[r2]) r2=i;
10.割点和桥
嗯,这个还是很重要的,可以找出双连通分量和边连通分量,其中边连通分量是可以缩点的,而且缩出来的是一颗树!我使用的是
割点
void dfs(int s,int f){ int childs=0; dfn[s]=low[s]=++tim;//时间戳 for(int i=fir[s];i;i=nex[i]) { int v=tar[i]; if(!dfn[v]) { childs++; dfs(v,i); if(low[v]>=dfn[s]) dot[s]=1; low[s]=min(low[s],low[v]); } else if(dfn[s]>=dfn[v]&&(i^1)!=f)//这里的(i^1)!=f是不说按原路返回即可 low[s]=min(low[s],dfn[v]); } if(!f&&childs==1) dot[s]=0;}
桥
void dfs(int s,int f){ dfn[s]=low[s]=++tim;//时间戳 for(int i=fir[s];i;i=nex[i]) { int v=tar[i]; if(!dfn[v]) { dfs(v,i); if(low[v]>dfn[s]) bridge[i]=bridge[i^1]=1; low[s]=min(low[s],low[v]); } else if(dfn[s]>=dfn[v]&&(i^1)!=f)//这里的(i^1)!=f是不说按原路返回即可 low[s]=min(low[s],dfn[v]); }}
11.
倍增,首先是处理出来任意节点
int getk(int r,int k){ for(int i=0;i<=20;i++) if(k&(1<<i)) r=f[r][i]; return r;}int getd(int r,int d){return getk(r,dep[r]-d);}int LCA(int a,int b){ if(dep[a]<dep[b]) swap(a,b); a=getd(a,dep[b]); if(a==b) return a; else { for(int j=20;j>=0;j--) if(f[a][j]!=f[b][j]) a=f[a][j],b=f[b][j]; return f[a][0]; }}
12.
网络流什么的,时间复杂度简直玄学啊(不对就是玄学),而且
(某一道题目,最下面的一个使用二分图匈牙利匹配,上面的所有使用网络流,对比之强烈可以看出……)
有些图论题目,网络流做了还有奇效,所以相对于匈牙利匹配,网络流唯一的劣势就是代码长了……但是只要背了就不会写错,打得很快的~(不过代码真的长到怀疑人生,尽管还是没有平衡树和高精度长啦……)
int aug(int s,int augco){ if(s==g) return augco; int augc=augco,delta,mind=g; for(int i=fir[s];i;i=nex[i]) { int v=tar[i]; if(cap[i]) { if(d[s]==d[v]+1) { delta=aug(v,min(augc,cap[i])); cap[i]-=delta; cap[i^1]+=delta; augc-=delta; if(!augc||d[w]==g) return augco-augc; } mind=min(mind,d[v]+1); } } if(augc==augco) { gd[d[s]]--; if(gd[d[s]]==0) d[w]=g; d[s]=mind; gd[d[s]]++; } return augco-augc;}int sap(int s){ memset(d,0,sizeof(d)); gd[0]=g;//d是距离,gd是距离汇点距离为i的点个数,g为总点数 while(d[s]<g) flow+=aug(s,inf); return flow;}
1.平衡树
灭
2.并查集
并查集其实是一个很好用的东西,可以快速查询合并两个集合,但是如果不优化,
int grand(int a){ if(!f[a]) return a; return f[a]=grand(f[a]);}int Union(int a,int b){ int p=grand(a),q=grand(b); if(p!=q) f[p]=q;}
1.最长上升公共子序列
本来是
for(int i=1;i<=n;i++){ int k=0; for(int j=1;j<=m;j++) { if(A[i]==B[j]) f[j]=max(f[j],k+1); if(A[i]>B[j]) k=max(k,f[j]); }}
3.归并排序
除了逆序对,这东西好像并没有什么用,但是还是有必要写一写
对一个区间
void m_sort(int l,int m,int r){ if(l==r) return; m_sort(l,(l+m)/2,m); m_sort(m+1,(m+1+r)/2,r); int i=l,j=m+1,k=l; for(;i<=m&&j<=r;) { if(A[i]<=A[j]) a[k++]=A[i],i++; else a[k++]=A[j],j++; } if(j<=r) while(j<=r) a[k++]=A[j],j++; if(i<=m) while(i<=m) a[k++]=A[i],i++; for(i=l;i<=r;i++) A[i]=a[i];}
1.读入优化
这玩意儿的重要性我不想再提,要知道这是可以把一个1200ms的程序挽救到600ms的东西
void read(int &p){ p=0; int f=0; char c=getchar(); while(c<'0'||c>'9') { if(c=='-')f=1; c=getchar(); } while(c>='0'&&c<='9') p=p*10+c-'0',c=getchar(); if(f)p=-p;}
2.逆序对(顺便就搞了归并排序)
这玩意就不多说了,自己领悟
void m_sort(int l,int m,int r){ if(l==r) return; m_sort(l,(l+m)/2,m); m_sort(m+1,(m+1+r)/2,r); int i=l,j=m+1,k=l; for(;i<=m&&j<=r;) { if(A[i]<=A[j]) a[k++]=A[i],i++; else { a[k++]=A[j],j++; sum+=m-i+1; } } if(j<=r) while(j<=r) a[k++]=A[j],j++; if(i<=m) while(i<=m) a[k++]=A[i],i++; for(i=l;i<=r;i++) A[i]=a[i];}
3.叉积
我本来想写几何总版的,但是由于懒啊,所以我还是决定就写一个叉积
double cross(Vector a,Vector b){return a.x*b.y-a.y*b.x;}
4.海伦公式
这玩意好像真的用不上,不过还是写一个,不然对不起模板大纲
s=(a+b+c)/2;S=sqrt(s*(s-a)*(s-b)*(s-c));
- 【复习】NOIP2017提高组-背板开始
- 【NOIP2017提高组】轰炸
- Noip2017提高组Day1
- NOIP2017提高组游记
- NOIP2017提高组小记
- 【总结】NOIP2017 提高组
- NOIP2017提高组总结
- NOIP2017 提高组 题解
- NOIP2017提高组总结
- NOIP2017提高组D1
- NOIP2017提高组D2
- NOIP2017提高组
- 【noip2017提高组总结】
- 【NOIP2017提高A组冲刺11.8】好文章 ——联赛字符串算法复习
- NOIP2017提高组初赛题解
- NOIP2017提高组预赛详解
- noip2017提高组初赛答案
- 【NOIP2017提高组】好路线
- 解决php7+nginx+yaf访问出现404问题
- 利用mmap()写一个拷贝文件的程序
- 设计模式-策略模式
- Selenium启动Chrome时配置选项
- javascript的一些运算符
- 【复习】NOIP2017提高组-背板开始
- NPOI 读公式生成的单元格数据为0
- CSS
- 贷款超市的“大秘密”:月利润上千万,APP为主战场
- Java注释@interface的用法【转】
- git的使用命令摘要
- java利用pol导出excel
- 【Codeforces696E】...Wait for it...
- HTML 链接