今天突发奇想来整理一些小的方法,一般都用来优化程序的(持续更新,想起啥就写啥)
快速幂
1 int pow(int x,int y)
2 {
3 int ans=1;
4 while(y){
5 if(y&1)ans*=x;
6 x*=x;y>>=1;
7 }
8 return ans;
9 }
10
11 快速幂
适用范围:这个一般求x的y次方都可以用这个东东,毕竟比正常的运算快的多,当然如果是较小数据可以直接用位运算
快速乘法
1 ll sum(ll x,ll y){
2 ll cnt=0;
3 while(y){
4 if(y&1){
5 cnt=cnt+x;
6 }y>>=1;x=x+x;
7 }return cnt;
8 }
这东西一般都是用于当x*y要对m取余,但是单纯的乘法会直接溢出
long double黑科技乘法
1 ll sum(ll x,ll y){
2 long double cnt=(long double)x*y;
3 long double d=(long double)cnt/m; ll zz=d+1e-6;
4 ll r=x*y-zz*m; r%=m; r+=m; r%=m;
5 return r;
6 }
这个的运用的位置和上一个类似,x*y对m取余会溢出的时候,虽然我不懂这东西的原理,只知道这是黑科技,效率高。
读入优化
1 int read(){
2 int x=0,f;char ch=getchar();
3 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
4 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
5 return x*f;
6 }
7
8 读入优化
适用范围:这个一般都是读入整数时使用的,读入快的多,大数据推荐使用,小数据就无所谓了
维护前缀和来剪枝(有人说这是利用了A*的思想,我不知道这到底该叫啥,不过看了就知道了)
1 int sum[maxn];//前缀和
2 void dfs(int now,int pos){
3 if(now+sum[n]-sum[pos]<=ans)return;
4 //当前的值加上后来可能会加上的所有值都小于目前的最优答案就跳出
5 ……………………………………
6 }
适用范围:比如在n个数选m个使总值最大的情况(有附加条件),用DFS的时候,如果当前的值+之后的所有值(假设后面的都选上)<=当前找出的最优值,就可以剪枝了
例题:vijos1048
并查集找环
1 int find(int x){
2 if(fa[x]==x)return x;
3 return find(fa[x]);
4 }
5
6 for(int i=1;i<=n;i++){
7 int u=e[i].u,v=e[i].v;
8 int fu=find(u),fv=find(v);
9 //fa[u]=fu;fa[v]=fv;这句话根据题意判断是否加
10 if(fu!=fv){
11 fa[fu]=fv;
12 }
13 }
其实这个是并查集的经典用法,或者说这就是并查集的基本操作,说他能找环是因为当fu==fv说明如果在当前的图上加上这条边就会出现一个环,这可以画图去感受
线形筛
1 for(int i=2;i<=n;i++){
2 if(!vis[i])prime[++tot]=i;
3 for(int j=1;j<=tot&&i*prime[j]<=n;j++){
4 vis[i*prime[j]]=1;
5 if(i%prime[j]==0)break;
6 }
7 }
这个比较常用于预处理一些带素数的题,如果在过程中判断一个数是不是素数和容易爆时间,所以预处理后只需要判断我们找出的素数包不包含我们正在询问的数
至于里面的break那句其实是为了优化这个预处理,因为是i*素数,所以就在素数的倍数的地方停下,因为循环到后面的时候还会对停下的地方后面进行操作,不需要在这里多浪费时间,这个可以自己画图模拟,还是很好想通的
欧拉筛
1 for(int i=2,j;i<=N;i++){ 2 if(!vis[i])prime[++tot]=i; 3 for(j=i*i;j<=N;j+=i){ 4 vis[j]=1; 5 } 6 }
这个东东可能看起来比较玄学,但其实这个操作是讲n范围内,i的倍数全部标记成了合数,然后j一开始是i*i是因为在之前i-1到2的这些循环中,已经把当前i的2到i-1倍标记过了
这东西适用范围和线型筛差不多,但是个人更喜欢用欧拉筛
杨辉三角
1 val[1][1]=1;
2 for(int i=2;i<=maxn;i++)
3 for(int j=1;j<=i;j++){
4 val[i][j]=val[i-1][j]+val[i-1][j-1];
5 }
这个东西一般比较适用于规律题,一个很典型的例子就是noip2016D2T1,赤裸裸的杨辉三角啊,不过那道题是从0,0开始的,那道题里的i,j位就是在i个数选j个数的方案数
所以考场上打个暴力画个表格真的很有必要
扩展欧几里得
1 void ex_gcd(int a,int b){
2 if(b==0){
3 x=1;y=0;return ;
4 }
5 ex_gcd(b,a%b);
6 x1=x;
7 x=y;
8 y=x1-(a/b)*y;
9 return ;
10 }
这个式子有点迷,当有ax ≡ 1 (mod b),ax+by=1,扩展欧几里得求出来的x,y就是这个式子里面的x,y,至于这个代码中x,y,x1的转换是可以根据换算来证明的,我的另一篇写扩欧的博客里有这个的简单证明,扩欧的例题:poj1061 vijos1781