[ACM]常用工具函数整理

来源:互联网 发布:淘宝红包图片 编辑:程序博客网 时间:2024/04/30 12:22

目录

高斯消元 1

整型高斯消元模板 2

浮点数高斯消元模板 6

字典树 8

最短路 9

模拟取余 15

最大子段和 16

最长递增子序列 18

组合数打表 20

快速幂 21

最大公约数 22

Next_permutation 23

N皇后 24

最长回文子串 25

大数加减乘除简易高效模板 26

组合数取模 30

Stirling 33

循环节 35

区间DP 35

拓扑排序 37

状态压缩位运算 38

字符串处理sscanf 40

数论相关 43

素数 44

进制转换 49

RMQ 50

次小生成树 52

最小树形图 55

哈希表 58

阶层的逆元,线性求逆元 59

n个球,m个盒子问题(公式解决) 60

积分公式题目 60

快速读入(正负都可读) 60

一些常数 60

 

高斯消元

a[0][0]*X0 + a[0][1] *X1 + a[0][2]*X2+...........a[0][n-1]*Xn-1   =  a[0][n] 
a[1][0]*X0 + a[1][1] *X1 + a[1][2]*X2+...........a[1][n-1]*Xn-1 ) =  a[1][n] 
..................................................
a[m-1][0]*X0 + a[m-1][1] *X1 + a[m-1][2]*X2+...........a[m-1][n-1]*Xn-1 = a[m-1][n]

一共有m个方程,有n个未知量(X0X1...XN-1),未知量为所求,a[0....m-1][n]为常数。

在一些ACM题目中关键点就是构造方程,在求解概率期望的时候经常用到,找到题目中的状态递推方程。

常用E[k]表示从k节点到达目标节点还需要的期望的步数,那么目标节点p,  E[p]=0,很关键。

构造出方程组,带入模板。

整型高斯消元模板

[cpp] view plaincopy

1. //高斯消元模板    

2. #include <iostream>    

3. #include <stdio.h>    

4. #include <string.h>    

5. #include <string>    

6. #include <cmath>    

7. using namespace std;    

8. const int maxn=105;    

9. int equ,var; // equ个方程,var个变元。增广阵行数为equ, 分别为0equ - 1,列数为var + 1,分别为0var.    

10. int a[maxn][maxn];    

11. int x[maxn]; // 解集.    

12. bool free_x[maxn]; // 判断是否是不确定的变元.    

13. int free_num;    

14. void Debug(void)    

15. {    

16.     int i,j;    

17.     for(i=0;i<equ;i++)    

18.     {    

19.         for(j=0;j<var+1;j++)    

20.         {    

21.             cout<<a[i][j]<<" ";    

22.         }    

23.         cout<<endl;    

24.     }    

25.     cout<<endl;    

26. }    

27. inline int gcd(int a, int b)    

28. {    

29.     int t;    

30.     while (b!=0)    

31.     {    

32.         t=b;    

33.         b=a%b;    

34.         a=t;    

35.     }    

36.     return a;    

37. }    

38. inline int lcm(int a, int b)    

39. {    

40.     return a*b/gcd(a,b);    

41. }    

42. // 高斯消元法解方程组(Gauss-Jordan elimination).(-2表示有浮点数解,但无整数解,-1表示无解,0表示唯一解,大于0表示无穷解,并返回自由变元的个数)    

43. int Gauss(void)    

44. {    

45.     int i,j,k;    

46.     int max_r; // 当前这列绝对值最大的行.    

47.     int col; // 当前处理的列.    

48.     int ta,tb;    

49.     int LCM;    

50.     int temp;    

51.     int free_x_num;    

52.     int free_index;    

53.     // 转换为阶梯阵.    

54.     col=0; // 当前处理的列.    

55.     for(k=0;k<equ&&col<var;k++,col++)    

56.     { // 枚举当前处理的行.    

57.         // 找到该col列元素绝对值最大的那行与第k行交换.(为了在除法时减小误差)    

58.         max_r=k;    

59.         for(i=k+1;i<equ;i++)    

60.         {    

61.             if(abs(a[i][col])>abs(a[max_r][col]))    

62.                 max_r=i;    

63.         }    

64.         if(max_r!=k)    

65.         { // 与第k行交换.    

66.             for(j=k;j<var+1;j++)    

67.                 swap(a[k][j],a[max_r][j]);    

68.         }    

69.         if(a[k][col]==0)    

70.         { // 说明该col列第k行以下全是0了,则处理当前行的下一列.    

71.             k--; continue;    

72.         }    

73.         for(i=k+1;i<equ;i++)    

74.         { // 枚举要删去的行.    

75.             if (a[i][col]!=0)    

76.             {    

77.                 LCM=lcm(abs(a[i][col]),abs(a[k][col]));    

78.                 ta=LCM/abs(a[i][col]),tb=LCM/abs(a[k][col]);    

79.                 if(a[i][col]*a[k][col]<0) tb=-tb; // 异号的情况是两个数相加.    

80.                 for(j=col;j<var+1;j++)    

81.                 {    

82.                     a[i][j]=a[i][j]*ta-a[k][j]*tb;    

83.                 }    

84.             }    

85.         }    

86.     }    

87.     //Debug();    

88.     // 1. 无解的情况化简的增广阵中存在(0, 0, ..., a)这样的行(a != 0).    

89.     for(i=k;i<equ;i++)    

90.     { // 对于无穷解来说,如果要判断哪些是自由变元,那么初等行变换中的交换就会影响,则要记录交换.    

91.         if (a[i][col]!=0)    

92.             return -1;    

93.     }    

94.     // 2. 无穷解的情况var * (var + 1)的增广阵中出现(0, 0, ..., 0)这样的行,即说明没有形成严格的上三角阵.    

95.     // 且出现的行数即为自由变元的个数.    

96.     if(k<var)    

97.     {    

98.         // 首先,自由变元有var - k个,即不确定的变元至少有var - k.    

99.         for (i=k-1;i>=0;i--)    

100.         {    

101.             // i行一定不会是(0, 0, ..., 0)的情况,因为这样的行是在第k行到第equ.    

102.             // 同样,第i行一定不会是(0, 0, ..., a), a != 0的情况,这样的无解的.    

103.             free_x_num=0; // 用于判断该行中的不确定的变元的个数,如果超过1个,则无法求解,它们仍然为不确定的变元.    

104.             for(j=0;j<var;j++)    

105.             {    

106.                 if(a[i][j]!=0&&free_x[j])    

107.                     free_x_num++,free_index = j;    

108.             }    

109.             if(free_x_num>1)    

110.                 continue// 无法求解出确定的变元.    

111.             // 说明就只有一个不确定的变元free_index,那么可以求解出该变元,且该变元是确定的.    

112.             temp=a[i][var];    

113.             for(j=0;j<var;j++)    

114.             {    

115.                 if(a[i][j]!=0&&j!=free_index)    

116.                 temp-=a[i][j]*x[j];    

117.             }    

118.             x[free_index]=temp/a[i][free_index]; // 求出该变元.    

119.             free_x[free_index]=0; // 该变元是确定的.    

120.         }    

121.         return var-k; // 自由变元有var - k.    

122.     }    

123.     // 3. 唯一解的情况var * (var + 1)的增广阵中形成严格的上三角阵.    

124.     // 计算出Xn-1, Xn-2 ... X0.    

125.     for (i=var-1;i>=0;i--)    

126.     {    

127.         temp=a[i][var];    

128.         for(j=i+1;j<var;j++)    

129.         {    

130.             if(a[i][j]!=0)     

131.                 temp-=a[i][j]*x[j];    

132.         }    

133.         if(temp%a[i][i]!=0)     

134.             return -2; // 说明有浮点数解,但无整数解.    

135.         x[i]=temp/a[i][i];    

136.     }    

137.     return 0;    

138. }    

139. int main(void)    

140. {    

141.     int i, j;    

142.     while (scanf("%d %d",&equ,&var)!=EOF)    

143.     {    

144.         memset(a,0,sizeof(a));    

145.         memset(x,0,sizeof(x));    

146.         memset(free_x,1,sizeof(free_x)); // 一开始全是不确定的变元    

147.             

148.         for(i=0;i<equ;i++)//构造增广矩阵    

149.             for(j=0;j<var+1;j++)    

150.                 scanf("%d",&a[i][j]);    

151. //        Debug();    

152.         free_num=Gauss();    

153.         if(free_num==-1) printf("无解!\n");    

154.         else if(free_num==-2) printf("有浮点数解,无整数解!\n");    

155.         else if(free_num>0)    

156.         {    

157.             printf("无穷多解自由变元个数为%d\n",free_num);    

158.             for(i=0;i<var;i++)    

159.             {    

160.                 if(free_x[i]) printf("x%d 是不确定的\n",i+1);    

161.                 else printf("x%d: %d\n",i+1,x[i]);    

162.             }    

163.         }    

164.         else    

165.         {    

166.             for(i=0;i<var;i++)    

167.                 printf("x%d: %d\n",i+1,x[i]);    

168.         }    

169.         printf("\n");    

170.     }    

171.     return 0;    

172. }  


浮点数高斯消元模板

新的,用下面这个:

[cpp] view plaincopy

1. const int maxn=1002;  

2. const double eps=1e-12;  

3. double a[maxn][maxn];  

4. int equ,var;//equ个方程,var个变量  

5. double x[maxn];//解集  

6. bool free_x[maxn];  

7. int n;  

8.   

9. int sgn(double x)  

10. {  

11.     return (x>eps)-(x<-eps);  

12. }  

13. // 高斯消元法解方程组(Gauss-Jordan elimination).(0表示无解,1表示唯一解,大于1表示无穷解,并返回自由变元的个数)  

14. int gauss()  

15. {  

16.     equ=n,var=n;//多少个方程,多少个变量  

17.     int i,j,k;  

18.     int max_r; // 当前这列绝对值最大的行.  

19.     int col; // 当前处理的列.  

20.     double temp;  

21.     int free_x_num;  

22.     int free_index;  

23.     // 转换为阶梯阵.  

24.     col=0; // 当前处理的列.  

25.     memset(free_x,true,sizeof(free_x));  

26.     for(k=0;k<equ&&col<var;k++,col++)  

27.     {  

28.         max_r=k;  

29.         for(i=k+1;i<equ;i++)  

30.         {  

31.             if(sgn(fabs(a[i][col])-fabs(a[max_r][col]))>0)  

32.                 max_r=i;  

33.         }  

34.         if(max_r!=k)  

35.         { // 与第k行交换.  

36.             for(j=k;j<var+1;j++)  

37.                 swap(a[k][j],a[max_r][j]);  

38.         }  

39.         if(sgn(a[k][col])==0)  

40.         { // 说明该col列第k行以下全是0了,则处理当前行的下一列.  

41.             k--; continue;  

42.         }  

43.         for(i=k+1;i<equ;i++)  

44.         { // 枚举要删去的行.  

45.             if (sgn(a[i][col])!=0)  

46.             {  

47.                 temp=a[i][col]/a[k][col];  

48.                 for(j=col;j<var+1;j++)  

49.                 {  

50.                     a[i][j]=a[i][j]-a[k][j]*temp;  

51.                 }  

52.             }  

53.         }  

54.     }  

55.   

56.     for(i=k;i<equ;i++)  

57.     {  

58.         if (sgn(a[i][col])!=0)  

59.             return 0;  

60.     }  

61.     if(k<var)  

62.     {  

63.         for(i=k-1;i>=0;i--)  

64.         {  

65.             free_x_num=0;  

66.             for(j=0;j<var;j++)  

67.             {  

68.                 if (sgn(a[i][j])!=0&&free_x[j])  

69.                     free_x_num++,free_index=j;  

70.             }  

71.             if(free_x_num>1) continue;  

72.             temp=a[i][var];  

73.             for(j=0;j<var;j++)  

74.             {  

75.                 if(sgn(a[i][j])!=0&&j!=free_index)  

76.                     temp-=a[i][j]*x[j];  

77.             }  

78.             x[free_index]=temp/a[i][free_index];  

79.             free_x[free_index]=0;  

80.         }  

81.         return var-k;  

82.     }  

83.   

84.     for (i=var-1;i>=0;i--)  

85.     {  

86.         temp=a[i][var];  

87.         for(j=i+1;j<var;j++)  

88.         {  

89.             if(sgn(a[i][j])!=0)  

90.                 temp-=a[i][j]*x[j];  

91.         }  

92.         x[i]=temp/a[i][i];  

93.     }  

94.     return 1;  

95. }  

字典树

1. struct Trie  

2. {  

3.     int cnt;//有多少单词经过该节点  

4.     Trie*next[27];  

5.     Trie()  

6.     {  

7.         cnt=0;  

8.         for(int i=0;i<27;i++)  

9.             next[i]=NULL;  

10.     }  

11. }root;  

12.   

13. void create(char *s)//将字符串s建立在trie树中  

14. {  

15.     Trie *p=&root;  

16.     int len=strlen(s);  

17.     for(int i=0;i<len;i++)  

18.     {  

19.         int id=s[i]-'a';//唯一标识  

20.         if(p->next[id]==NULL)  

21.         {  

22.             p->next[id]=new Trie;  

23.             p->next[id]->cnt++;  

24.         }  

25.         else  

26.             p->next[id]->cnt++;  

27.         p=p->next[id];  

28.     }  

29. }  

30.   

31. int find(char *s)//查找字符串s是多少单词的前缀。  

32. {  

33.     Trie *p=&root;  

34.     int len=strlen(s);  

35.     for(int i=0;i<len;i++)  

36.     {  

37.         int id=s[i]-'a';  

38.         if(p->next[id]==NULL)  

39.             return 0;  

40.         p=p->next[id];  

41.     }  

42.     return p->cnt;  

43. }  

最短路

[cpp] view plaincopy

1. //bellman_ford算法,求单源到其它节点的最短路,可以处理含有负权的边,并且能判断图中是否存在负权回路(这一条在一些题中也有应用)  

2. //无向图转化为有向图,边数加倍,构造边结构体,没用到邻接矩阵  

3. #include <iostream>  

4. using namespace std;  

5. const int maxNodeNum=110;//最多节点个数  

6. const int maxEdgeNum=10010;//最多边条数  

7. int nodeNum,edgeNum;//节点,有向边个数  

8. int dis[maxNodeNum];//从单源点到各个点的距离  

9. const int inf=0x3f3f3f3f;//边的权重无穷大数  

10. bool loop;//判断是否存在负权回路  

11.   

12. struct Edge  

13. {  

14.     int s,e,w;  

15. }edge[maxEdgeNum*2];//构造边,这里因为是无向图,要看成有向处理。  

16.   

17. void bellman_ford(int start)  

18. {  

19.     //第一步:赋初值  

20.     for(int i=1;i<=nodeNum;i++)  

21.         dis[i]=inf;  

22.     dis[start]=0;  

23.     //第二步,对边进行松弛更新操作  

24.     for(int i=1;i<=nodeNum-1;i++)//最短路径为简单路径不可能含有环,图中最多有nodeNum-1条边  

25.     {  

26.         bool ok=0;  

27.         for(int j=1;j<=edgeNum;j++)  

28.         {  

29.             if(dis[edge[j].s]+edge[j].w<dis[edge[j].e])//松弛  

30.             {  

31.                 dis[edge[j].e]=dis[edge[j].s]+edge[j].w;  

32.                 ok=1;  

33.             }  

34.         }  

35.         if(ok==0)  

36.             break;  

37.     }  

38.     //第三步,判断图中是否存在负权环  

39.     loop=0;  

40.     for(int i=1;i<=edgeNum;i++)  

41.         if(dis[edge[i].s]+edge[i].w<dis[edge[i].e])  

42.         loop=1;  

43. }  

44.   

45. int main()//125MS  

46. {  

47.     while(cin>>nodeNum>>edgeNum&&(nodeNum||edgeNum))  

48.     {  

49.         int from,to,w;  

50.         int cnt=1;  

51.         for(int i=1;i<=edgeNum;i++)//无向图,一条无向边看为两条有向边  

52.         {  

53.             cin>>from>>to>>w;  

54.             edge[cnt].s=edge[cnt+1].e=from;  

55.             edge[cnt].e=edge[cnt+1].s=to;  

56.             edge[cnt++].w=w;  

57.             edge[cnt++].w=w;//切记,不能写成 edge[cnt++]=edge[cnt++].w  

58.         }  

59.         edgeNum*=2;//无向图  

60.         bellman_ford(1);  

61.         cout<<dis[nodeNum]<<endl;  

62.     }  

63.     return 0;  

64. }  

 

 

[cpp] view plaincopy

1. //SPFA算法,是对bellman_ford算法的优化,采用队列,在队列中取点对其相邻的点进行松弛操作  

2. //如果松弛成功且相邻的点不在队列中,就把相邻的点加入队列,被取的点出队,并把它的状态(是否在队列中)  

3. //改为否,vis[i]=0,同一个节点可以多次进入队列进行松弛操作  

4. //这样的题操作步骤:首先建立邻接矩阵,邻接矩阵初始化为inf,注意需要判断一下输入的边是否小于已有的边,取最小的那个,因为可能有重边,  

5. //建立完邻接矩阵,写SPFA函数,dis[]数组初始化为inf,源点dis[start]=0  

6. #include <iostream>  

7. #include <string.h>  

8. #include <queue>  

9. using namespace std;  

10. const int maxNodeNum=110;//最多节点个数  

11. const int maxEdgeNum=10010;//最多边条数  

12. const int inf=0x3f3f3f3f;//边的权重无穷大数  

13. int nodeNum,edgeNum;//节点,有向边个数  

14. int dis[maxNodeNum];//从单源点到各个点的距离  

15. bool vis[maxNodeNum];//某个节点是否已经在队列中  

16.   

17. int mp[maxNodeNum][maxNodeNum];//建立邻接矩阵  

18.   

19. void SPFA(int start)  

20. {  

21.     //第一步:建立队列,初始化vis,dis数组,并把源点加入队列中,修改其vis[]状态  

22.     queue<int>q;  

23.     memset(vis,0,sizeof(vis));  

24.     for(int i=1;i<=nodeNum;i++)  

25.         dis[i]=inf;  

26.     dis[start]=0;  

27.     q.push(start);  

28.     vis[start]=1;  

29.     //第二步:在队列中取点,把其vis状态设为0,对该点相邻的点(连接二者的边)进行松弛操作,修改相邻点的dis[]  

30.     //并判断相邻的点vis[]状态是否为0(不存在于队列中),如果是,将其加入到队列中  

31.     while(!q.empty())  

32.     {  

33.         int from=q.front();  

34.         q.pop();  

35.         vis[from]=0;//别忘了这一句,哎  

36.         for(int i=1;i<=nodeNum;i++)  

37.         {  

38.             if(dis[from]+mp[from][i]<dis[i])  

39.             {  

40.                 dis[i]=dis[from]+mp[from][i];  

41.                 if(!vis[i])//要写在松弛成功的里面  

42.                 {  

43.                     q.push(i);  

44.                     vis[i]=1;  

45.                 }  

46.             }  

47.         }  

48.     }  

49. }  

50.   

51. int main()//109MS  

52. {  

53.     while(cin>>nodeNum>>edgeNum&&(nodeNum||edgeNum))  

54.     {  

55.         int from,to,w;  

56.         memset(mp,inf,sizeof(mp));//初始化  

57.         for(int i=1;i<=edgeNum;i++)//无向图,一条无向边看为两条有向边  

58.         {  

59.             cin>>from>>to>>w;  

60.             if(w<mp[from][to])  

61.                 mp[from][to]=mp[to][from]=w;  

62.         }  

63.         SPFA(1);  

64.         cout<<dis[nodeNum]<<endl;  

65.     }  

66.     return 0;  

67. }  


[cpp] view plaincopy

1. //floyed算法,时间复杂度高,但代码简单,可以处理负边,但图中不能包含负权回路  

2. //可以求任意一点到另外一点的最短路,而不只是源点唯一  

3.   

4. #include <iostream>  

5. #include <string.h>  

6. #include <queue>  

7. using namespace std;  

8. const int maxNodeNum=110;//最多节点个数  

9. const int maxEdgeNum=10010;//最多边条数  

10. const int inf=0x3f3f3f3f;  

11. int nodeNum,edgeNum;//节点,有向边个数  

12. int mp[maxNodeNum][maxNodeNum];//建立邻接矩阵  

13.   

14. void floyed()  

15. {  

16.     for(int k=1;k<=nodeNum;k++)  

17.         for(int i=1;i<=nodeNum;i++)  

18.             for(int j=1;j<=nodeNum;j++)  

19.                 if(mp[i][k]+mp[k][j]<mp[i][j])  

20.                     mp[i][j]=mp[i][k]+mp[k][j];  

21. }  

22.   

23. int main()//140MS  

24. {  

25.     while(cin>>nodeNum>>edgeNum&&(nodeNum||edgeNum))  

26.     {  

27.         int from,to,w;  

28.         memset(mp,inf,sizeof(mp));//初始化  

29.         for(int i=1;i<=edgeNum;i++)//无向图,一条无向边看为两条有向边  

30.         {  

31.             cin>>from>>to>>w;  

32.             if(w<mp[from][to])  

33.                 mp[from][to]=mp[to][from]=w;  

34.         }  

35.         floyed();  

36.         cout<<mp[1][nodeNum]<<endl;  

37.     }  

38.     return 0;  

39. }  


[cpp] view plaincopy

1. //dijkstra算法求最短路,单源最短路,不能处理带有负权的图  

2. //思想为单源点加入集合,更新dis[]数组,每次取dis[]最小的那个点,加入集合,再次更新dis[]数组,取点加入集合,直到所有的点都加入集合中  

3. #include <iostream>  

4. #include <string.h>  

5. #include <queue>  

6. using namespace std;  

7. const int maxNodeNum=110;//最多节点个数  

8. const int maxEdgeNum=10010;//最多边条数  

9. const int inf=0x3f3f3f3f;  

10. int nodeNum,edgeNum;//节点,有向边个数  

11. int mp[maxNodeNum][maxNodeNum];//建立邻接矩阵  

12. int dis[maxNodeNum];//dis[i]为源点到i的最短路径  

13. bool vis[maxNodeNum];//判断某个节点是否已加入集合  

14.   

15. void dijkstra(int start)  

16. {  

17.     //**第一步:初始化,dis[]为最大,vis均为0(都未加入集合)  

18.     memset(dis,inf,sizeof(dis));  

19.     memset(vis,0,sizeof(vis));  

20.     dis[start]=0;    //<span style="color:#ff0000;">注意不能写vis[start]=1,因为这时候第一个节点还没有被访问,下面循环中,第一个选择的就是第一个节点,切记</span>  

21.     //**第二步:找dis[]值最小的点,加入集合,并更新与其相连的点的dis[]  

22.       

23.     //一开始集合里没有任何点,下面的循环中,第一个找到的点肯定是源点  

24.     for(int i=1;i<=nodeNum;i++)  

25.     {  

26.         //寻找dis[]最小的点,加入集合中  

27.         int MinNumber,Min=inf;//MinNumberdis[]值最小的点的编号  

28.         for(int j=1;j<=nodeNum;j++)  

29.         {  

30.             if(dis[j]<Min&&!vis[j])  

31.             {  

32.                 Min=dis[j];  

33.                 MinNumber=j;  

34.             }  

35.         }  

36.         //找到dis[]最小的点,加入集合,更新与其相连的点的dis  

37.         vis[MinNumber]=1;  

38.         for(int j=1;j<=nodeNum;j++)  

39.             if(dis[MinNumber]+mp[MinNumber][j]<dis[j])  

40.             dis[j]=dis[MinNumber]+mp[MinNumber][j];  

41.     }  

42. }  

43.   

44.   

45. int main()//109MS  

46. {  

47.     while(cin>>nodeNum>>edgeNum&&(nodeNum||edgeNum))  

48.     {  

49.         int from,to,w;  

50.         memset(mp,inf,sizeof(mp));//初始化  

51.         for(int i=1;i<=edgeNum;i++)//无向图,一条无向边看为两条有向边  

52.         {  

53.             cin>>from>>to>>w;  

54.             if(w<mp[from][to])  

55.                 mp[from][to]=mp[to][from]=w;  

56.         }  

57.         dijkstra(1);  

58.         cout<<dis[nodeNum]<<endl;  

59.     }  

60.     return 0;  

61. }  

模拟取余

给一个9进制数,求这个数模8的余数。数的长度不超过107次方

1. string str;  

2.     int c=1;  

3.     while(cin>>str&&str!="0")  

4.     {  

5.         int len=str.length();  

6.         int num=0;  

7.         for(int i=0;i<len;i++)  

8.         {  

9.             num=(num*9+(str[i]-'0'))%8;  

10.         }  

11.         cout<<"Case "<<c++<<": "<<num<<endl;  

12.     }  

最大子段和

当所有数都为负数时,最大子段和为0.

[cpp] view plaincopy

1. int MaxSum(int num[],int n)  

2. {  

3.     int sum=0,b=0;  

4.     int i;  

5.     for (i=1;i<=n;i++)  

6.     {  

7.         if(b>0)  

8.             b+=num[i];  

9.         else  

10.             b=num[i];  

11.         if(b>sum)  

12.             sum=b;  

13.     }  

14.     return sum;  

15. }  

只求最大和,没有保存位置。

保存位置:start为起 end为末

[cpp] view plaincopy

1. int start=1,end=1,s=1,e=1;  

2. int sum=0,max=num[1];//不能让max=0  

3.  for(int i=1;i<=n;i++)  

4.  {  

5.      e=i;  

6.      sum=sum+num[i];  

7.      if(max<sum)  

8.      {  

9.          max=sum;  

10.          start=s;  

11.          end=e;  

12.      }  

13.      if(sum<0)  

14.      {  

15.          s=i+1;  

16.          sum=0;  

17.      }  

18.  }  


 

本题需要保存起始位置。下面代码如果全是负数,输出最小的那个位置

代码:

[cpp] view plaincopy

1. #include <iostream>  

2. using namespace std;  

3. const int maxn=100002;  

4. int num[maxn];  

5. int n;  

6. int main()  

7. {  

8.     int t;cin>>t;int c=1;  

9.     while(t--)  

10.     {  

11.         cin>>n;  

12.         for(int i=1;i<=n;i++)  

13.             cin>>num[i];  

14.         int start=1,end=1,s=1,e=1;//这里start end一定要赋值为1  

15.         int sum=0,max=num[1];//不能让max=0  

16.         for(int i=1;i<=n;i++)  

17.         {  

18.             e=i;  

19.             sum=sum+num[i];  

20.             if(max<sum)  

21.             {  

22.                 max=sum;  

23.                 start=s;  

24.                 end=e;  

25.             }  

26.             if(sum<0)  

27.             {  

28.                 s=i+1;  

29.                 sum=0;  

30.             }  

31.         }  

32.         cout<<"Case "<<c++<<":"<<endl;  

33.         cout<<max<<" "<<start<<" "<<end<<endl;  

34.         if(t)  

35.             cout<<endl;  

36.     }  

37.     return 0;  

38. }  

最长递增子序列 lower_bound

1. int dp[20];  

2. const int inf=0x7fffffff;  

3. int a[7]={2,1,3,4,8,5,9};  

4. const int maxn=500005;  

5. int main()  

6. {  

7.     fill(dp,dp+7,inf);  

8.     for(int i=0;i<7;i++)  

9.     {  

10.         *lower_bound(dp,dp+7,a[i])=a[i];  

11.     }  

12.     int len=lower_bound(dp,dp+7,inf)-dp;  

13.     for(int i=0;i<len;i++)  

14.         cout<<dp[i]<<endl;

分隔符

1. //int num[8]={0,0,3,1,2,4,1,5};//原始序列  

2. int num[8]={0,1,3,4,2,5,6,7};  

3. int d[8];//保存最长递增子序列的元素,第二种方法,复杂度低  

4. int dp[8];//第一种方法,复杂度高,dp[i]存储从num[1]num[i]之间最长递增子序列的长度,num[i]一定在里边  

5. int BinSearch(int key,int i,int low,int high)//二分搜索,找到road[i]d[]数组中的位置  

6. {  

7.     int l=low,r=high;  

8.     while(l<=r)  

9.     {  

10.         int mid=(l+r)/2;  

11.         if(num[i]<=d[mid])  

12.             r=mid-1;  

13.         else  

14.             l=mid+1;  

15.     }  

16.     return l;  

17. }  

18. int LIS()  

19. {  

20.     d[1]=num[1];  

21.     int len=1;//最长递增子序列的元素个数  

22.     int j;  

23.     for(int i=2;i<=7;i++)  

24.     {  

25.         if(d[len]<num[i])  

26.             j=++len;  

27.         else  

28.             j=BinSearch(num[i],i,1,len);  

29.         d[j]=num[i];  

30.     }  

31.     return len;//d[]数组中保存的原始序列中的最长递增子序列的元素个数  

32. }  

33.   

34. /*int LIS(int num[],int n) 

35. { 

36.     int ans=1; 

37.     int m; 

38.     dp[1]=1; 

39.     for(int i=2;i<=n;i++) 

40.     { 

41.         m=0; 

42.         for(int j=1;j<i;j++) 

43.         { 

44.             if(dp[j]>m&&num[j]<num[i]) 

45.                 m=dp[j]; 

46.         } 

47.         dp[i]=m+1; 

48.         if(dp[i]>ans) 

49.             ans=dp[i]; 

50.     } 

51.     return ans; 

52. }*/  

53. int main()  

54. {  

55.     //第二种方法,复杂度低,二分搜索,可以保存最长递增子序列的元素  

56.     int len=LIS();  

57.     cout<<len<<endl;  

58.     for(int i=1;i<=len;i++)  

59.        cout<<d[i]<<endl;//输出原始序列中构成最长递增子序列的元素。*/  

60.   

61.     //第一种方法,复杂度高,只能返回最长递增子序列的长度  

62.     /*int len=LIS(num,7); 

63.     cout<<len<<endl; 

64.     for(int i=1;i<=7;i++) 

65.         cout<<dp[i]<<endl;*/  

66.     return 0;  

67. }  

 

组合数打表

1. int c[11][11];  

2.   

3. void init()  

4. {  

5.     memset(c,0,sizeof(c));  

6.     c[0][0]=c[1][0]=c[1][1]=1;  

7.     for(int i=2;i<=10;i++)  

8.     {  

9.         c[i][0]=c[i][i]=1;  

10.         for(int j=1;j<i;j++)  

11.             c[i][j]=c[i-1][j]+c[i-1][j-1];  

12.     }  

13. }  

快速幂

1. int mi(int d,int n)//快速幂运算   思想 24次方可以 2的平方乘以2的平凡  

2. {  

3.     int ans=1;  

4.     while(n)  

5.     {  

6.         if(n%2==1)  

7.             ans=ans*d%mod;//这条语句肯定执行的  

8.         d=d*d%mod;  

9.         n/=2;  

10.     }  

11.     return ans;  

12. }  

int n,mod;

struct mat

{

    ll arr[3][3];

    mat()

    {

        memset(arr,0,sizeof(arr));

    }

};

 

mat mul(mat a,mat b)

{

    mat ans;

    for(int i=0;i<3;i++)

        for(int k=0;k<3;k++)

        {

            if(a.arr[i][k])

            {

                for(int j=0;j<3;j++)

                {

                    ans.arr[i][j]+=(a.arr[i][k]*b.arr[k][j]);//这一句超int类型

                    if(ans.arr[i][j]>=mod)

                        ans.arr[i][j]%=mod;

                }

            }

        }

    return ans;

}

 

mat power(mat p,int k)

{

    if(k==1) return p;

    mat e;

    for(int i=0;i<3;i++)

        e.arr[i][i]=1;

    if(k==0) return e;

    while(k)

    {

        if(k&1)

            e=mul(e,p);

        p=mul(p,p);

        k>>=1;

    }

    return e;

}

最大公约数

1. int gcd(int a,int b)  

2. {  

3.     int t=1,c,d;  

4.     while(a!=b)  

5.     {  

6.         if(a<b)  

7.             swap(a,b);  

8.         if(!(a&1))//如果a为偶数 a&1=0  

9.         {  

10.             a>>=1;  

11.             c=1;//a为偶数的标志  

12.         }  

13.         else  

14.             c=0;  

15.         if(!(b&1))//如果b为偶数   

16.         {  

17.             b>>=1;  

18.             d=1;//b为偶数的标志  

19.         }  

20.         else  

21.             d=0;  

22.         if(c&&d)//a,b都为偶数  

23.             t<<=1;//公因子  

24.         else if(!c&&!d//a,b都为奇数  

25.             a-=b;  

26.     }  

27.     return t*a;  

28. }  

29. 

30. int  gcd(int a,int b)  

31. {  

32.     return b==0?a:gcd(b,a%b);  

1. }  int gcd(int a,int b)  

2. {  

3.     if(!a)  

4.         return b;  

5.     int c;  

6.     while(b)  

7.     {  

8.         c=b;  

9.         b=a%b;  

10.         a=c;  

11.     }  

12.     return a;  

13. }  

33. 

Next_permutation生成全排列

1. char a[3]={'a','b','c'};//第一个排列保证正序,有时候根据题目要求,需要对其进行排序处理。  

2.     for(int i=1;i<=6;i++)//i为总共排列的个数  ,及 3  

3.     {  

4.         for(int j=0;j<3;j++)  

5.             cout<<a[j]<<" ";  

6.         cout<<endl;  

7.         next_permutation(a,a+3);//放在第一个排列的后边,输出第一个排列的下一个排列  

8.   

9.     }  

10.     cout<<"*******************"<<endl;  

11.     int b[4]={0,1,2,3};  

12.     for(int i=1;i<=6;i++)  

13.     {  

14.         for(int j=1;j<=3;j++)  

15.             cout<<b[j]<<" ";  

16.         cout<<endl;  

17.         next_permutation(b+1,b+1+3);  

18.     }  

19.     cout<<"*******************"<<endl;  

20.     int c[3]={1,2,3};  

21.     for(int i=1;i<=6;i++)  

22.     {  

23.         for(int j=0;j<3;j++)  

24.         cout<<c[j]<<" ";  

25.         cout<<endl;  

26.         next_permutation(c,c+3);  

27.     }  

28.     return 0;  

29. }  

N皇后 search(0)

1. int c[11],tot,n;  

2.   

3. void search(int cur)//不符合条件就回溯  

4. {  

5.     int i,j;  

6.     if(cur==n) tot++;//search(n-1)时也成功,说明有符合题意的情况,所以search(n-1)里面有search(n)执行时,tot++  

7.     else for(i=0;i<n;i++)//当前行放在第几列上。  

8.     {  

9.         int ok=1;  

10.         c[cur]=i;//当前行放在第几列上。  

11.         for(j=0;j<cur;j++)//j<cur不是j<i一定要注意!!!  

12.             if(c[cur]==c[j]||cur-c[cur]==j-c[j]||cur+c[cur]==j+c[j])//检查是否在同一列(列数相等),左对角线(行数-列数相等),右对角线(行数+列数相等)  

13.         {  

14.             ok=0;  

15.             break;//不行,尝试放在下一列上。跳到(i=0;i<n;i++)那里去。  

16.         }//该循环只起到检查的作用  

17.         if(ok)  

18.             search(cur+1);//当前可以放在该列,搜索下一行,但注意,当前行的最外层循环还没有跑完,即当前行放在哪一列还没有完全的遍历,当下一行搜索不成功时,回溯到当前行继续放在下一列。  

19.     }  

20. }  

最长回文子串

 

包括标点符号,空格和大小写字母,输出最常回文串,忽略字母的大小写,空格和符号。

比如字符串 sabc,  , Cba    输出abc,  ,Cba

1. string s;  

2. char buf[maxn];//存放字符串中的字母,且存放的都是大写字母(原串中的小写字母被转换成大写字母,判断回文串时容易判断)  

3. int p[maxn];//存放原串中第i个字母在原串中的位置  

4.   

5. int main()  

6. {  

7.     getline(cin,s);  

8.     int m=0;//m为原串中字母的个数  

9.     int max=0;  

10.     int x,y;//原串中最长回文串的起始和终止的位置  

11.     int n=s.length();  

12.     for(int i=0;i<n;i++)  

13.     {  

14.         if(isalpha(s[i]))  

15.         {  

16.             p[m]=i;  

17.             buf[m++]=toupper(s[i]);  

18.         }  

19.     }  

20.     for(int i=0;i<m;i++)//枚举i,i为回文串中最中间那个字母的位置  

21.     {  

22.         for(int j=0;i-j>=0&&i+j<m;j++)//原串中字母的个数为奇数时  

23.         {  

24.             if(buf[i-j] !=buf[i+j])  

25.             break;//注意不要写成continue  

26.             if(j*2+1>max){max=j*2+1;x=p[i-j];y=p[i+j];}//注意不要写成x=i-jy=i+j  

27.         }  

28.         for(int j=0;i-j>=0&&i+j+1<m;j++)//原串中字母的个数为偶数时比如,abba  

29.         {  

30.             if(buf[i-j]!=buf[i+j+1])  

31.                 break;  

32.             if(j*2+2>max)  

33.             {  

34.                 max=j*2+2;x=p[i-j];y=p[i+j+1];  

35.             }  

36.         }  

37.     }  

38.     for(int i=x;i<=y;i++)  

39.         cout<<s[i];//输出原串中的一段回文区间,注意是s[i],原串中的。  

40.     cout<<endl;  

41.     return 0;  

42. }  

大数加减乘除简易高效模板

void add(char* a,char* b,char* c)

{

    int i,j,k,max,min,n,temp;

    char *s,*pmax,*pmin;

    max=strlen(a);

    min=strlen(b);

    if (max<min)

    {

        temp=max;

        max=min;

        min=temp;

        pmax=b;

        pmin=a;

    }

    else

    {

        pmax=a;

        pmin=b;

    }

    s=(char*)malloc(sizeof(char)*(max+1));

    s[0]='0';

    for (i=min-1,j=max-1,k=max;i>=0;i--,j--,k--)

        s[k]=pmin[i]-'0'+pmax[j];

       for (;j>=0;j--,k--)

           s[k]=pmax[j];

    for (i=max;i>=0;i--)

        if (s[i]>'9')

        {

            s[i]-=10;

            s[i-1]++;

        }

    if (s[0]=='0')

    {

        for (i=0;i<=max;i++)

            c[i-1]=s[i];

           c[i-1]='\0';

    }

    else

    {

        for (i=0;i<=max;i++)

            c[i]=s[i];

           c[i]='\0';

    }

    free(s);

}

 

/*大数减法*/

void subtract(char* a,char* b,char* c)

{

    int i,j,ca,cb;

    ca=strlen(a);

    cb=strlen(b);

    if (ca>cb||(ca==cb&&strcmp(a,b)>=0))

    {

        for (i=ca-1,j=cb-1;j>=0;i--,j--)

            a[i]-=(b[j]-'0');

           for (i=ca-1;i>=0;i--)

               if (a[i]<'0')

               {

                   a[i]+=10;

                   a[i-1]--;

            }

        i=0;

        while (a[i]=='0')

            i++;

           if (a[i]=='\0')

          {

              c[0]='0';

              c[1]='\0';

        }

        else

        {

            for (j=0;a[i]!='\0';i++,j++)

                c[j]=a[i];

               c[j]='\0';

        }

    }

    else

    {

        for (i=ca-1,j=cb-1;i>=0;i--,j--)

            b[j]-=(a[i]-'0');

           for (j=cb-1;j>=0;j--)

               if (b[j]<'0')

               {

                   b[j]+=10;

                   b[j-1]--;

            }

        j=0;

        while (b[j]=='0')

            j++;

           i=1;

           c[0]='-';

           for (;b[j]!='\0';i++,j++)

               c[i]=b[j];

          c[i]='\0';

    }

}

 

/* 大数乘法*/

void multiply(char* a,char* b,char* c)

{

    int i,j,ca,cb,* s;

    ca=strlen(a);

    cb=strlen(b);

    s=(int*)malloc(sizeof(int)*(ca+cb));

    for (i=0;i<ca+cb;i++)

        s[i]=0;

    for (i=0;i<ca;i++)

        for (j=0;j<cb;j++)

            s[i+j+1]+=(a[i]-'0')*(b[j]-'0');

    for (i=ca+cb-1;i>=0;i--)

        if (s[i]>=10)

        {

            s[i-1]+=s[i]/10;

            s[i]%=10;

        }

    i=0;

    while (s[i]==0)

        i++;

       for (j=0;i<ca+cb;i++,j++)

           c[j]=s[i]+'0';

    c[j]='\0';

    free(s);

}

 

/*大数除法,返回余数*/

int dividor(char* a,int b,char* c)

{

    int i,j,temp=0,n;

    char* s;

    n=strlen(a);

    s=(char*)malloc(sizeof(char)*(n+1));

    for (i=0;a[i]!=0;i++)

       {

           temp=temp*10+a[i]-'0';

           s[i]=temp/b+'0';

           temp%=b;

    }

    s[i]='\0';

    for (i=0;s[i]=='0'&&s[i]!='\0';i++);

    if (s[i]=='\0')

    {

        c[0]='0';

        c[1]='\0';

    }

    else

    {

        for (j=0;s[i]!='\0';i++,j++)

            c[j]=s[i];

           c[j]='\0';

    }

    free(s);

    return temp;

}

const int maxn=2000;

char s[maxn],t1[maxn],t2[maxn],t3[maxn];

char a[17],b[17],c[17];

char ans[maxn];

 

int main()

{

    a[0]='8';

    a[1]='\0';

    b[0]='7';

    b[1]='\0';

    c[0]='1';

    c[1]='\0';

    int t;

    int cas=0;

    scanf("%d",&t);

    getchar();

    while(t--)

    {

        memset(ans,'\0',sizeof(ans));

        gets(s);

        multiply(s,s,t1);//s^2放在t1里面

        multiply(t1,a,t2);//8*n^2

        multiply(b,s,t3);//7*n

        subtract(t2,t3,ans);//8*n^2-7*n

        add(ans,c,ans);//8*n^2-7*n+1

        printf("Case #%d: %s\n",++cas,ans);

    }

    return 0;

}

组合数取模

Cn,m%p

1. const int maxn=100002;  

2. ll n,m,p;  

3. ll fac[maxn];  

4.   

5. void getfac(ll p)//预处理阶层  

6. {  

7.     fac[0]=1;  

8.     for(int i=1;i<=p;i++)  

9.         fac[i]=fac[i-1]*i%p;  

10. }  

11.   

12. ll power(ll a,ll n,ll p)//快速幂运算  

13. {  

14.     ll ans=1;  

15.     while(n)  

16.     {  

17.         if(n&1)  

18.             ans=ans*a%p;  

19.         a=a*a%p;  

20.         n/=2;  

21.     }  

22.     return ans;  

23. }  

24.   

25. ll lucas(ll n,ll m,ll p)  

26. {  

27.     ll ans=1;  

28.     while(n&&m)  

29.     {  

30.         ll a=n%p;  

31.         ll b=m%p;  

32.         if(a<b) return 0;  

33.         ans=(ans*fac[a]*power(fac[b]*fac[a-b]%p,p-2,p))%p;//  fac[b]*fac[a-b]后面别忘了%p,否则WA  

34.         n/=p;  

35.         m/=p;  

36.     }  

37.     return ans;  

38. }  

39.   

40.   

41. int main()  

42. {  

43.     int t;cin>>t;  

44.     while(t--)  

45.     {  

46.         cin>>n>>m>>p;  

47.         getfac(p);  

48.         cout<<lucas(n+m,m,p)<<endl;  

49.     }  

50.     return 0;  

51. }  

 

1.利用整数唯一分解定理,求(n+1-m) * (n+m)!  /  ( m! * (n+1)!  )

任何正整数都有且只有一种方法写出其素因子幂相乘的形式。比如18= 2 * 3^2

A=(p1^k1)*(p2^k2)*(p3^k3)*(p4^k4)*......*(pn^kn)   pi为素数

还有把阶层看作一个数,m! 怎样求m!里面素数2的指数呢?

cnt=0;   while(m)  {  m/=2; cnt+=m; }  就可以了,为什么呢?考虑m=4,则m!=  4*3*2*1, 第一次m/=2,是计算m!里面有多少个数能整除2的(有42,所以cnt+=2,有两个数贡献

了两个素数2,接下来第二次m/=2,是计算m!里面有多少个数能整除4的,有1个数又贡献了一个素数2.

所以先预先筛出最大范围内的素数,然后考虑每个素数就好了,关键是求出整个式子的该素数的指数是多少。

1. bool isprime[maxn*2+10];  

2. int prime[maxn*2+10];  

3. int len=0;//素数的个数  

4. int n,m;  

5. void sieve(int n)//n以内的素数  

6. {  

7.     for(int i=0;i<=n;i++)  

8.         isprime[i]=1;  

9.     isprime[0]=isprime[1]=0;  

10.     for(int i=2;i<=n;i++)  

11.         if(isprime[i])  

12.         {  

13.             prime[len++]=i;  

14.             for(int j=2*i;j<=n;j+=i)  

15.                 isprime[j]=0;  

16.         }  

17. }  

18. int cal(int p,int n)//计算n!里面有多少个p相乘  

19. {  

20.     int ans=0;  

21.     while(n)  

22.     {  

23.         n/=p;  

24.         ans+=n;  

25.     }  

26.     return ans;  

27. }  

28.   

29. int main()  

30. {  

31.         sieve(maxn*2);  

32.         long long ans=1;//记得要用long long  

33.         cin>>n>>m;  

34.         int nm=n+1-m;  

35.         for(int i=0;i<len&&prime[i]<=(n+m);i++)//prime[i]<=(n+m)是因为拆成素数幂相乘的形式该素数不会大于n+m,最大(n+m)!   (n+m)*(n+m-1)*(n+m-2).....  

36.         {  

37.             int cnt=0;//分解为素数prime[i]的指数是多少  

38.             while(nm%prime[i]==0)//nm中有多少个prime[i],也就是把nm分解后prime[i]的指数  

39.             {  

40.                 nm/=prime[i];  

41.                 cnt++;  

42.             }  

43.             cnt=cnt+cal(prime[i],n+m)-cal(prime[i],m)-cal(prime[i],n+1);//加上分子的指数再减去分母的指数  

44.             for(int j=1;j<=cnt;j++)  

45.             {  

46.                 ans=ans*prime[i];  

47.                 if(ans>=mod)  

48.                     ans%=mod;  

49.             }  

50.         }  

51.         cout<<ans<<endl;  

52.         return 0;  

53. }  

Stirling数

.第二类Stirling

        定理:第二类StirlingS(p,k)计数的是把p元素集合划分到k个不可区分的盒子里且没有空盒子的划分个数。

        证明:元素在拿些盒子并不重要,唯一重要的是各个盒子里装的是什么,而不管哪个盒子装了什么。

        递推公式有:S(p,p)=1 (p>=0)         S(p,0)=0  (p>=1)         S(p,k)=k*S(p-1,k)+S(p-1,k-1)   (1<=k<=p-1) 。考虑将前p个正整数,12.....p的集合作为要被划分的集合,把

{1,2,.....p}分到k个非空且不可区分的盒子的划分有两种情况:

       (1)那些使得p自己单独在一个盒子的划分,存在有S(p-1,k-1)种划分个数

       (2)那些使得p不单独自己在一个盒子的划分,存在有 k*S(p-1,k)种划分个数

        考虑第二种情况,p不单独自己在一个盒子,也就是p和其他元素在一个集合里面,也就是说在没有放p之前,有p-1个元素已经分到了k个非空且不可区分的盒子里面(划

分个数为S(p-1,k),那么现在问题是把p放在哪个盒子里面呢,有k种选择,所以存在有k*S(p-1,k)

       模板:

[cpp] view plaincopy

1. long long s[maxn][maxn];//存放要求的Stirling  

2. const long long mod=1e9+7;//取模  

3.   

4. void init()//预处理  

5. {  

6.     memset(s,0,sizeof(s));  

7.     s[1][1]=1;  

8.     for(int i=2;i<=maxn-1;i++)  

9.         for(int j=1;j<=i;j++)  

10.     {  

11.         s[i][j]=s[i-1][j-1]+j*s[i-1][j];  

12.         if(s[i][j]>=mod)  

13.             s[i][j]%=mod;  

14.     }  

15. }  

       注意:要用long long类型,当元素个数>20,就超int类型了。

       扩展:k! *S(p,k) 计数的是把p元素集合划分到k个可区分的盒子里且没有空盒子的划分个数。

.Bell

        定理:BellB(p)是将p元素集合分到非空且不可区分盒子的划分个数(没有说分到几个盒子里面)。

        B(p)=S(p,0)+S(p,1)+.....+S(p,k) 

       所以要求Bell数就要先求出第二类Stiring数。

.第一类Stirling

       定理:第一类Stirlings(p,k)计数的是把p个对象排成k个非空循环排列的方法数。

       证明:把上述定理叙述中的循环排列叫做圆圈。递推公式为:

       s(p,p)=1 (p>=0)    p个人和p个圆圈,每个圆圈就只有一个人

       s(p,0)=0 (p>=1)    如果至少有1个人,那么任何的安排都至少包含一个圆圈

       s(p,k)=(p-1)*s(p-1,k)+s(p-1,k-1)

       设人被标上1,2,.....p。将这p个人排成k个圆圈有两种情况。第一种排法是在一个圆圈里只有标号为p的人自己,排法有s(p-1,k-1)个。第二种排法中,p至少和另一个人在一

个圆圈里。这些排法可以通过把1,2....p-1排成k个圆圈再把p放在1,2....p-1任何一人的左边得到,因此第二种类型的排法共有(p-1)*s(p-1,k)种排法。

       在证明中我们所做的就是把{1,2,...,p}划分到k个非空且不可区分的盒子,然后将每个盒子中的元素排成一个循环排列。

       模板:

[cpp] view plaincopy

1. long long s[maxn][maxn];//存放要求的第一类Stirling  

2. const long long mod=1e9+7;//取模  

3.   

4. void init()//预处理  

5. {  

6.     memset(s,0,sizeof(s));  

7.     s[1][1]=1;  

8.     for(int i=2;i<=maxn-1;i++)  

9.         for(int j=1;j<=i;j++)  

10.     {  

11.         s[i][j]=s[i-1][j-1]+(i-1)*s[i-1][j];  

12.         if(s[i][j]>=mod)  

13.             s[i][j]%=mod;  

14.     }  

15. }  

循环节

1. int n;  

2. int num[210];//输入的整数  

3. char s[210];//输入的字符串  

4. bool vis[210];//计算循环节时用到  

5. int cir[210][210];//cir[i][0]表示第i个循环节的长度,cir[i]表示第i个循环节里面包括什么元素  

6. int cnt;//循环节的个数  

7. int k;//循环的个数  

8.   

9. int getCircle()  

10. {  

11.     cnt=0;  

12.     memset(vis,0,sizeof(vis));  

13.     for(int i=1;i<=n;i++)  

14.     {  

15.         int len=0;//循环节的长度  

16.         int temp=i;  

17.         while(!vis[temp])  

18.         {  

19.             cir[cnt][++len]=temp;  

20.             vis[temp]=true;  

21.             temp=num[temp];  

22.         }  

23.         if(len)//别忘了加这一句话  

24.             cir[cnt++][0]=len;  

25.     }  

26.     return cnt;  

27. }  

区间DP

春希非常爱管闲事,他每天都会抽空帮助一些同学,由于春希非常死板,出于公平性,春希不会先帮助后来找他的同学。

现在有n个同学需要他的帮助,虽然他很想一天之类帮助所有人,但毕竟精力有限,于是他决定分m天来帮助他们。

根据事情的重要性,春希帮助不同同学会有不同的快乐值,而春希获得的总的快乐值为每天获得的快乐值的乘积。

现在给出nm,以及帮助完各同学时获得的快乐值,求春希能获得的最大快乐值。

Input

第一行为一个整数T,代表数据组数。

每组数据,第一行两个整数n,m。表示需要帮助的同学的数量,和天数。(1≤m≤min(n,10),1≤n≤20)

第二行为n个整数,表示帮助这个同学的获得的快乐值,每个快乐值不大于5

Output

每组数据输出一行,一个整数,表示最大的快乐值。

Sample input and output

Sample Input

Sample Output

1

5 3

3 2 1 4 5

125

解题思路:

dp[j][i]表示前j个数分为i部分的和的乘积的最大值。测试用例中(3+2)*(1+4)*5=125

三重循环。

dp[j][i]=max(dp[j][i],dp[k][i-1]*(sum[j]-sum[k]));   

代码:

#include <iostream>

#include <algorithm>

#include <string.h>

using namespace std;

const int maxn=25;

int dp[maxn][maxn];

int num[maxn],sum[maxn];

int t,n,m;

 

int main()

{

    cin>>t;

    while(t--)

    {

        cin>>n>>m;

        for(int i=0;i<=n;i++)

            for(int j=0;j<=m;j++)

            dp[i][j]=1;

        sum[0]=0;

        for(int i=1;i<=n;i++)

        {

            cin>>num[i];

            sum[i]=sum[i-1]+num[i];

        }

        for(int i=1;i<=m;i++)

            for(int j=n;j>=i;j--)

                for(int k=i-1;k<j;k++)

        {

            dp[j][i]=max(dp[j][i],dp[k][i-1]*(sum[j]-sum[k]));

        }

        cout<<dp[n][m]<<endl;

    }

    return 0;

}

拓扑排序

首先建图,单向边,谁在前面,就由谁指向另一个。每次找一个入度为0的点,该点的次序确定,与之相连的点入读都减1,再重新找

入度为0的点,重复操作,每次确定一个点,故需要找n次。 当找不到入读为0的点时,则说明有环。

 

const int maxn=510;

int mp[maxn][maxn];//存图

int indegree[maxn];//保存每个点的入读

int ans[maxn];//存放一组答案

int from,to;//有关系的两个点,建边由from指向to

int n,m;//n个顶点,m个关系

bool flag;//判断是否存在环,1表示无环,0表示有环

void topo()

{

    memset(indegree,0,sizeof(indegree));

    memset(mp,0,sizeof(mp));

    while(m--)

    {

        scanf("%d%d",&from,&to);

        if(!mp[from][to])//需要判断一下是否有重边

        {

            indegree[to]++;

            mp[from][to]=1;

        }

    }

    int cnt=0;

    flag=1;

    int i,j;

    for(i=1;i<=n;i++)//n

    {

        for(j=1;j<=n;j++)//找入度为0的点

        {

            if(indegree[j]==0)

            {

                ans[cnt++]=j;

                indegree[j]--;

                for(int k=1;k<=n;k++)//与其相连的边

                    if(mp[j][k])

                    indegree[k]--;

                break;

            }

        }

        if(j==n+1)//该次查找找不到入度为0的点

        {

            flag=0;

            break;

        }

    }

}

状态压缩位运算

左移<<

x<<k

也就是把x的每一位向左移动k位的结果,右边不够加0

x<<1相当于x*2x<<K表示的是向左移动k位,相当于乘k2

 

右移>>

和上面相反

 

或运算|

x|y表示如下

x:   101

y:   010

x|y: 111

也就是将xy表示成二进制,然后按位做或运算

 

与运算&

也就是将xy表示成二进制,然后按位做与运算

 

非运算~

也就是将x表示成二进制,然后按位做取反运算

 

异或运算^

也就是将xy表示成二进制,然后按位做异或运算,相同为0,不同为1

 

获取一个或多个固定位的值

假设x=1010

要获取从右边数第1位的值(从0位开始),可以x&(1<<1)也就是

x&(1<<1)也就是

x:        1010

1<<1:     0010

x&(1<<1)  0010  及右数第一位是1    用到了1&0=0 0&0=0其他位置0,1&1=1,0&1=0求特定位是多少的性质

这样我们就可以通过判断x&(1<<1)是否等于0来知道这一位是0还是1

也可以用x&(3<<2)获得右数第二位和第三位的值

 

把一个或多个固定位的值置0

我们要把右边数第1位的值置0

假设x=1010

x&(~(1<<1))

x:          1010

~(1<<1)     1101

x&(~(1<<1)) 1001    用到了 1&1=1  0&1=0  保持其他为不变,1&0=0,0&0=0特定位置0的性质

当然也可以用x&(~(3<<2))把右数第二位和第三位置0

 

把一个或多个固定位的值取反

假设x=1010

我们要把右边数第一位的值取反

x^(1<<1)

x         1010

1<<1      0010

x^(1<<1)  1000      用到了异或相同为1,不同为0的性质

当然也可以用x^(3<<2)把右数第二位和第三位取反

 

x^(~(1<<1))

x             1010

~(1<<1)       1101

x^(~(1<<1))   0111   x除右数第一位以外的其他位取反

 

判断第i个元素是否属于集合S if(s>>i&1)

向集合中加入第i个元素   S|1<<i

向集合中去除第i个元素   S&(~(1<<i))

 

判断一个数二进制中有没有两个相邻的1

if(x&(x<<1))  或者 if(x&(x>>1)) 相邻的1的话结果为1

取得二进制最右边的1,其它都变为0 ,  x & (~x + 1)   比如 x = 10101  ~x = 01010    ~x+1 = 01011    x & (~x + 1) = 00001   

将二进制最右边的1变为0 ,  x & ( x - 1) 

 

字符串处理sscanf

#include <iostream>

#include <stdio.h>

#include <string.h>

using namespace std;

int main()

{

    //字符串比较

    char st1[17]="hello";

    char st2[10]="hello";

    int re=strcmp(st1,st2);//re==0则说明两个字符串相等

    cout<<re<<endl;

    char st3[10];

    strcpy(st3,st1);

    cout<<st3<<endl;//输出hello,长度为5

    /*strcpy memcpy

 

    strcpymemcpy都是标准C库函数,它们有下面的特点。

    strcpy提供了字符串的复制。即strcpy只用于字符串复制,并且它不仅复制字符串内容之外,还会复制字符串的结束符。

    已知strcpy函数的原型是:char* strcpy(char* dest, const char* src);

    memcpy提供了一般内存的复制。即memcpy对于需要复制的内容没有限制,因此用途更广。

    char * strcpy(char * dest, const char * src) // 实现srcdest的复制

    void *memcpy(void *memTo, const void *memFrom, size_t size)

 

    strcpymemcpy主要有以下3方面的区别。

    1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。

    2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。

    3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy

    */

 

    //字符串截取子串substr

    string ori="nevergiveup";

    string now=ori.substr(4,5);//从第4个字符开始,长度为5, 注意ori字符是从0开始数的,所以第4个字符为r

    cout<<now<<endl;//输出rgive, 可以省略第二个参数5,那么一直到结尾

 

    //**************

    char str1[100];

    int data=1024;

    sprintf(str1,"%d",data);//将整型的data转化为字符串,字符串长度为4

    cout<<str1<<endl;//输出1024

    sprintf(str1,"%d",-89);

    cout<<str1<<endl;//输出-89,可重复利用,如果是-0,那么str10,不是-0

    char s1[10]="hello",s2[10]="world";

    sprintf(str1,"%s %s",s1,s2);//链接两个字符串,注意%s中间有空格,那么链接后也有空格

    cout<<str1<<endl;//输出hello world 长度为11

    sprintf(str1,"%X",17);//获取数的十六进制

    cout<<str1<<endl;//输出11,长度为2

    sprintf(str1,"%o",12);//获取数的八进制

    cout<<str1<<endl;//输出14,长度为2

    //**************

 

    /*

    原型: int sscanf (const char *str,const char * format,........);

    说明:sscanf()会将参数str的字符串根据参数format字符串来转换并格式化数据。转换后的结果存于对应的参数内。

         成功则返回参数数目,失败则返回0

    注意:sscanfscanf类似,都是用于输入的,只是后者以键盘(stdin)为输入源,前者以固定字符串为输入源。

    大家都知道sscanf是一个很好用的函数,利用它可以从字符串中取出整数、浮点数和字符串等等。它的使用方法简单,特别对于整数和浮点数来说。

    sscanf可以支持格式字符%[]

    (1)-: 表示范围,如:%[1-9]表示只读取1-9这几个数字 %[a-z]表示只读取a-z小写字母,类似地 %[A-Z]只读取大写字母

    (2)^: 表示不取,如:%[^1]表示读取除'1'以外的所有字符 %[^/]表示除/以外的所有字符

    (3),: 范围可以用","相连接 如%[1-9,a-z]表示同时取1-9数字和a-z小写字母

    (4)原则:从第一个在指定范围内的数字开始读取,到第一个不在范围内的数字结束%s 可以看成%[] 的一个特例 %[^ ](注意^后面有一个空格!)

    */

 

    char str[100]="2009:04:01";

    int year,month,day;

    sscanf(str,"%d:%d:%d",&year,&month,&day);

    cout<<year<<" "<<month<<" "<<day<<endl;

    //解析网址的例子

    const char str2[100]="http://www.google.com:1234";

    char protocol[32],host[100],port[10];

    sscanf(str2,"%[^:]://%[^:]:%[1-9]",protocol,host,port);

    cout<<protocol<<" "<<host<<" "<<port<<endl;//输出http www.google.com 1234

    char strr[12]="-1.2a24";//strr中提取浮点数到tp,遇到其他字符就停止

    double tp;

    sscanf(strr,"%lf",&tp);//注意&tp,地址符

    cout<<tp<<endl;//输出-1.2 这时候tp里面的值就为浮点数-1.2

    int tpp;

    sscanf("1000ac1","%d",&tpp);

    cout<<tpp<<endl;//输出1000

 

 

    sscanf("12345","%4s",str);//截取固定长度的字符串

    cout<<str<<endl;//输出1234

 

    //%*d 和 %*s 加了星号 (*) 表示跳过此数据不读入. (也就是不把此数据读入参数中)

    sscanf("12345acc","%*d%s",str);

    cout<<str<<endl;//输出aac

 

    //取到指定字符为止的字符串。如在下例中,取遇到'+'为止字符串。

    sscanf("12345+acc","%[^+]",str);

    cout<<str<<endl;//输出12345

 

    //取到指定字符集为止的字符串。如在下例中,取遇到小写字母为止的字符串。

    sscanf("12345+acc121","%[^a-z]",str);

    cout<<str<<endl;//输出12345+

    return 0;

}

 

/*字符串以空格分隔

新的方法,采用stl。第二种方法有些题目提交通不过….

string str;

    while(getline(cin,str)&&str!="#")

    {

        cout<<str<<endl;

        vc.clear();

        istringstream stream(str);

        string word;

        set<string>st;

        while(stream>>word)

        {

            vc.push_back(word);

        }

        int len=vc.size();

        for(int i=0;i<len;i++)

            cout<<vc[i]<<"  "<<vc[i].length()<<endl;

}

将输入的字符串以按空格分隔成若干单词,存到vc里面去。可以处理以空格开头的字符串.

 

第二种方法.

#include <iostream>

#include <stdio.h>

#include <string.h>

using namespace std;

 

int main()

{

    char str[1000];

    char re[1200][1000];

    gets(str);

    int len=0;

    sscanf(str,"%s",re[len++]);

    for(int i=strlen(re[len-1]);i<strlen(str)-1;i++)//也是遍历整个字符串

    {

        if(str[i]==' '&&str[i+1]!=' ')

            sscanf(&str[i],"%s",re[len++]);

    }

    for(int i=0;i<len;i++)

        cout<<re[i]<<"   len  "<<strlen(re[i])<<endl;

}

输入

hello bye goo glex moto134 x++00 ...

输出

hello   len  5

bye   len  3

goo   len  3

glex   len  4

moto134   len  7

x++00   len  5

...   len  3

 

*/

数论相关

1. long long  exgcd(long long a,long long b,long long &x,long long &y)//扩展欧几里得算法,返回a,b的最大公约数,ax+by=gcd(a,b)x,y为方程的一组解  

2. {  

3.     if(b==0)  

4.     {  

5.         x=1;  

6.         y=0;  

7.         return a;  

8.     }  

9.     long long d=exgcd(b,a%b,x,y);  

10.     long long t=x;  

11.     x=y;  

12.     y=t-a/b*y;  

13.     return d;  

14. }  

15. 

16. long long modular_liner_equation(long long a,long long b,long long n)//求解模线性方程ax=b(mod n)  

17. {  

18.     long long x,y,x0;  

19.     long long d=exgcd(a,n,x,y);  

20.     if(b%d)//没有解  

21.         return -1;  

22.     x0=(x*(b/d))%n;//特解  

23.     //for(int i=1;i<d;i++)  

24.         //cout<<(x0+i*(n/d))%n;  

25.     long long ans=x0,s=n/d;  

26.     ans=(ans%s+s)%s;   //ans为最小整数解  

27.     return ans;  

28. }  

 

素数

1.单独判断一个数是否为素数

[cpp] view plaincopy

1. bool prime(int n)  

2. {  

3.     if(n==0||n==1) return false;  

4.     if(n==2) return true;  

5.     for(int i=2;i<=sqrt(n);i++)  

6.         if(n%i==0)  

7.             return false;  

8.     return true;  

9. }  


2.筛法筛素数 ,求小于maxn的素数

isprime[ ] 保存小于maxn的数是否为素数,false表示不是素数,true表示素数

prime[ ] 保存小于maxn的素数有哪些,从0开始,长度为len

[cpp] view plaincopy

1. const int maxn=100;  

2. bool isprime[maxn];  

3. int prime[maxn];  

4. int len=0;  

5.   

6. void sieve(int n)  

7. {  

8.     for(int i=0;i<n;i++)  

9.         isprime[i]=1;  

10.     isprime[0]=isprime[1]=0;  

11.     for(int i=2;i<n;i++)  

12.         if(isprime[i])  

13.         {  

14.             prime[len++]=i;  

15.             for(int j=2*i;j<n;j+=i)  

16.                 isprime[j]=0;  

17.         }  

18. }  

19. //主函数调用sieve(maxn)  


3.如果只要求小于maxn的素数有哪些,去掉isprime[ ]数组

下面模板中 prime[ ] 中保存的是maxn中的素数有哪些,标号从1开始,总的素数个数为prime[0] ,它包含的素数范围为 prime[1]prime[  prime[0] ] 

[cpp] view plaincopy

1. const int maxn=100;  

2. int prime[maxn+1];  

3.   

4. void getPrime()  

5. {  

6.     memset(prime,0,sizeof(prime));//一开始prime都设为0代表都是素数(反向思考)  

7.     for(int i=2;i<=maxn;i++)  

8.     {  

9.         if(!prime[i])  

10.             prime[++prime[0]]=i;  

11.         for(int j=1;j<=prime[0]&&prime[j]<=maxn/i;j++)  

12.         {  

13.             prime[prime[j]*i]=1;//prime[k]=1k不是素数  

14.             if(i%prime[j]==0)  

15.                 break;  

16.         }  

17.     }  

18. }  

4.大区间筛素数

POJ2689 

给出一个区间[L,R], 范围为1<=L< R<=2147483647,区间长度长度不超过1000000

求距离最近和最远的两个素数(也就是相邻的差最小和最大的素数)

筛两次,第一次筛出11000000的素数,因为1000000^2已经超出int范围,这样的素数足够了。

函数getPrim();   prime[ ] 存第一次筛出的素数,总个数为prime[0] 

第二次利用已经筛出的素数去筛L,R之间的素数

函数getPrime2();     isprime[] 判断该数是否为素数 prime2[ ]筛出的素数有哪些,一共有prime2[0]

模板:

[cpp] view plaincopy

1. #include <iostream>  

2. #include <string.h>  

3. #include <stdio.h>  

4. #include <cmath>  

5. #include <algorithm>  

6. using namespace std;  

7.   

8. const int maxn=1e6;  

9. int prime[maxn+10];  

10.   

11. void getPrime()  

12. {  

13.     memset(prime,0,sizeof(prime));//一开始prime都设为0代表都是素数(反向思考)  

14.     for(int i=2;i<=maxn;i++)  

15.     {  

16.         if(!prime[i])  

17.             prime[++prime[0]]=i;  

18.         for(int j=1;j<=prime[0]&&prime[j]<=maxn/i;j++)  

19.         {  

20.             prime[prime[j]*i]=1;//prime[k]=1k不是素数  

21.             if(i%prime[j]==0)  

22.                 break;  

23.         }  

24.     }  

25. }  

26.   

27. bool isprime[maxn+10];  

28. int prime2[maxn+10];  

29.   

30. void getPrime2(int L,int R)  

31. {  

32.     memset(isprime,1,sizeof(isprime));  

33.     //isprime[0]=isprime[1]=0;//这句话不能加,考虑到左区间为2的时候,加上这一句,素数2,3会被判成合数  

34.     if(L<2) L=2;  

35.     for(int i=1;i<=prime[0]&&(long long)prime[i]*prime[i]<=R;i++)  

36.     {  

37.         int s=L/prime[i]+(L%prime[i]>0);//计算第一个比L大且能被prime[i]整除的数是prime[i]的几倍,从此处开始筛  

38.         if(s==1)//很特殊,如果从1开始筛的话,那么2会被筛成非素数  

39.             s=2;  

40.         for(int j=s;(long long)j*prime[i]<=R;j++)  

41.             if((long long)j*prime[i]>=L)  

42.             isprime[j*prime[i]-L]=false//区间映射 ,比如区间长度为4的区间[4,7],映射到[0,3]中,因为题目范围2,147,483,647数组开不出来  

43.     }  

44.     prime2[0]=0;  

45.     for(int i=0;i<=R-L;i++)  

46.         if(isprime[i])  

47.         prime2[++prime2[0]]=i+L;  

48. }  

49.   

50. int main()  

51. {  

52.     getPrime();  

53.     int L,R;  

54.     while(scanf("%d%d",&L,&R)!=EOF)  

55.     {  

56.         getPrime2(L,R);  

57.         if(prime2[0]<2)  

58.             printf("There are no adjacent primes.\n");  

59.         else  

60.         {  

61.             //for(int i=1;i<=prime2[0];i++)  

62.                // cout<<prime2[i]<<endl;  

63.             int x1=0,x2=1000000,y1=0,y2=0;  

64.             for(int i=1;i<prime2[0];i++)  

65.             {  

66.                 if(prime2[i+1]-prime2[i]<x2-x1)  

67.                 {  

68.                     x1=prime2[i];  

69.                     x2=prime2[i+1];  

70.                 }  

71.                 if(prime2[i+1]-prime2[i]>y2-y1)  

72.                 {  

73.                     y1=prime2[i];  

74.                     y2=prime2[i+1];  

75.                 }  

76.             }  

77.             printf("%d,%d are closest, %d,%d are most distant.\n",x1,x2,y1,y2);  

78.         }  

79.     }  

80.     return 0;  

81. }  

5.判断一个数是否是素数,并保存每个素数的素因子

const int N=110;

vector<int> x[N];

bool is[N];

 

void prime() {

    memset(is, false, sizeof(is));

    for (int i=0; i<N; i++) x[i].clear();

    for (int j=2; j<N; j+=2) x[j].push_back(2);

    for (int i=3; i<N; i+=2)

        if (!is[i]) {

            for (int j=i; j<N; j+=i) {

            //如果这里写成int j=i,那么is[]数组是不准确的,但x[j]里面是正确的,如果j是素数,那么x[j]里面就存了一个因子j

            //如果这里写成int j=2*i,那么is[]数组是准确的,但x[j]里面不正确,如果j是素数,那么x[j]里面为空,其他都是和上面

            //一样的.因此,如果is[]要用到的话,就采用第二种方法,对于素数的因子单独判断一下就可以了,如果is[]数组用不到,就采用

            //第一种方法。

                is[j] = true;

                x[j].push_back(i);

            }

        }

}

进制转换

输入一个十进制数N,将它转换成R进制数输出。

Input

输入数据包含多个测试实例,每个测试实例包含两个整数N(32位整数)R2<=R<=16, R<>10)。

Output

为每个测试实例输出转换后的数,每个输出占一行。如果R大于10,则对应的数字规则参考16进制(比如,10A表示,等等)。

Sample Input

7 2

23 12

-4 3

Sample Output

111

1B

-11

 

使用栈,因为有负数,所以符号单独输出。

#include <iostream>  

#include <cmath>  

#include <stack>  

using namespace std;  

int main()  

{  

    int r,n;  

    while(cin>>n>>r)  

    {  

        int mul;  

        mul=n/abs(n*1.0);  

        n=abs(n*1.0);  

        stack<int>s;  

        while(n)  

        {  

            s.push(n%r);  

            n/=r;  

        }  

        if(mul==-1)  

            cout<<'-';  

        while(!s.empty())  

        {  

            char m;  

            if(s.top()>=10)  

            {  

                m='A'+s.top()-10;  

                cout<<m;  

            }  

            else  

                cout<<s.top();  

            s.pop();  

        }  

        cout<<endl;  

    }  

    return 0;  

}

 

RMQ

/**

ST算法是另外一种高效的求解区间最值的算法。nlgn的处理,可以达到o(1)的查询,而且线段树的常数也是非常大的。

它是一种动态规划的方法。

以最小值为例。a为所寻找的数组.

用一个二维数组f(i,j)记录区间[i,i+2^j-1](持续2^j)区间中的最小值。其中f[i,0] = a[i];

所以,对于任意的一组(i,j)f(i,j) = min{f(i,j-1),f(i+2^(j-1),j-1)}来使用动态规划计算出来。

这个算法的高明之处不是在于这个动态规划的建立,而是它的查询:它的查询效率是O(1).

假设我们要求区间[m,n]a的最小值,找到一个数k使得2^k<n-m+1.

这样,可以把这个区间分成两个部分:[m,m+2^k-1][n-2^k+1,n].我们发现,这两个区间是已经初始化好的.

前面的区间是f(m,k),后面的区间是f(n-2^k+1,k).

这样,只要看这两个区间的最小值,就可以知道整个区间的最小值!

 

 

rmq求区间的最小值或者最大值

注意区间输入的n个数用num数组保存,下标为1.

maxRMQ(int l,int r)直接调用,返回区间[l,r]之间的最大值

minRMQ(int l,int r)直接调用,返回区间[l,r]之间的最小值

poj 2019

*/

#include <iostream>

#include <stdio.h>

#include <algorithm>

#include <string.h>

#include <stdlib.h>

#include <cmath>

#include <iomanip>

#include <vector>

#include <set>

#include <map>

#include <stack>

#include <queue>

#include <cctype>

#define rd(x) scanf("%d",&x)

#define rd2(x,y)  scanf("%d%d",&x,&y)

#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)

using namespace std;

typedef long long ll;

const int inf=0x3f3f3f3f;

const int maxn=50010;

int dpmax[maxn][20];//最大值20的确定由区间长度maxn确定,要求2j次方大于等于maxn就可以了

int dpmin[maxn][20];//最小值

int mm[maxn];//界限,2的多少次方

int num[maxn];//输入的数字,下标从1开始

int n,q;//n个数字,q个查询

 

void initRMQ(int n,int b[])//最小值最大值都有,如果只求一个最值,把另外条语句删掉即可

{

    mm[0]=-1;

    for(int i=1;i<=n;i++)

    {

        mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];

        dpmax[i][0]=b[i];

        dpmin[i][0]=b[i];

    }

    for(int j=1;j<=mm[n];j++)

        for(int i=1;i+(1<<j)-1<=n;i++)

    {

        dpmax[i][j]=max(dpmax[i][j-1],dpmax[i+(1<<(j-1))][j-1]);

        dpmin[i][j]=min(dpmin[i][j-1],dpmin[i+(1<<(j-1))][j-1]);

    }

}

 

int maxRMQ(int l,int r)

{

    int k=mm[r-l+1];

    return max(dpmax[l][k],dpmax[r-(1<<k)+1][k]);

}

 

int minRMQ(int l,int r)

{

    int k=mm[r-l+1];

    return min(dpmin[l][k],dpmin[r-(1<<k)+1][k]);

}

 

 

int main()

{

    while(rd2(n,q)!=EOF)

    {

        for(int i=1;i<=n;i++)

            rd(num[i]);

        initRMQ(n,num);

        while(q--)

        {

            int l,r;

            rd2(l,r);

            printf("%d\n",maxRMQ(l,r)-minRMQ(l,r));

        }

    }

    return 0;

}

 

次小生成树

求最小生成树时,用maxval[i][j],表示最小生成树中ij路径中的最大边权

求完后,直接枚举所有不在最小生成树中的边(比如该边的两端是i,j

然后替换掉路径i,j中最大边权的边,更新答案

点的编号从0开始。

 

为什么可以替换、

求完最小生成树后,从ij有路径,那么路径上的点两两可以相互到达,如果去掉最大边

,那么该路径就切成了两部分,而加入不在最小生成树的一条边(比如该边地两端是i,j

,那么又把这两部分重新串起来了,重新变得两两可以相互到达。

*/

typedef long long ll;

const int maxn=110;

const int inf=0x3f3f3f3f;

bool vis[maxn];

int lowc[maxn];

int pre[maxn];

int cost[maxn][maxn];

int maxval[maxn][maxn];

bool used[maxn][maxn];

 

int Prim(int cost[][maxn],int n)

{

    int ans=0;

    memset(vis,0,sizeof(vis));

    memset(maxval,0,sizeof(maxval));

    memset(used,0,sizeof(used));

    vis[0]=true;

    pre[0]=-1;

    for(int i=1;i<n;i++)

    {

        lowc[i]=cost[0][i];

        pre[i]=0;

    }

    lowc[0]=0;

    for(int i=1;i<n;i++)

    {

        int minc=inf,p=-1;

        for(int j=0;j<n;j++)

        {

            if(!vis[j]&&minc>lowc[j])

            {

                minc=lowc[j];

                p=j;

            }

        }

        if(minc==inf)

            return -1;

        ans+=minc;

        vis[p]=true;

        used[p][pre[p]]=used[pre[p]][p]=true;

        for(int j=0;j<n;j++)

        {

            if(vis[j])

                maxval[j][p]=maxval[p][j]=max(maxval[j][pre[p]],lowc[p]);

            //为什么这么写,vis[j]代表那么节点已经访问过了,该条边是不能加的,有没有边都没关系,否则成了环,

            //那么在路径j->p中,j是路径的起点,p就是路径的终点,而maxval[j][pre[p]],就是起点jp的上一个节点

            //组成的路径的最大边权值,lowc[p],则是带有p的那一边的权值,二者最大值,就是路径j->p的最大权值

            //比如 1->2->3->4-------->1,虚线代表有边或者没有边都行,那么此时p=4,j=1, pre[p]=3,

            //maxval[j][pre[p]]=maxval[1][3],也就是路径13上的最大边的权值,lowc[4]则是34上权值,二者的最大值

            //则是路径1->4的最大边的权值。

            if(!vis[j]&&lowc[j]>cost[p][j])

            {

                lowc[j]=cost[p][j];

                pre[j]=p;

            }

        }

    }

    return ans;

}

 

int ans;//保存最小生成树的值

 

int smst(int cost[][maxn],int n)//次小生成树

{

    int minc=inf;

    for(int i=0;i<n;i++)

        for(int j=i+1;j<n;j++)

    {

        if(cost[i][j]!=inf&&!used[i][j])//i,j之间有边,且该边没有在最小生成树中

        {

            minc=min(minc,ans+cost[i][j]-maxval[i][j]);//用该边替换最小生成树中i,j路径中的最大边

        }

    }

    if(minc==inf)

        return -1;

    return minc;

}

int n,m;

 

int main()

{

    int t;rd(t);

    while(t--)

    {

        rd2(n,m);

        memset(cost,inf,sizeof(cost));

        int u,v,w;

        while(m--)

        {

            rd3(u,v,w);

            u--;

            v--;

            if(w<cost[u][v])

                cost[u][v]=cost[v][u]=w;

        }

        ans=Prim(cost,n);

        if(ans==smst(cost,n))

            printf("Not Unique!\n");

        else

            printf("%d\n",ans);

    }

    return 0;

}

最小树形图

有向图的最小生成树。

const int N = 110;

const int inf = 0x3f3f3f3f;

 

struct node

{

int u, v;

double w;

}edge[N * N];

 

struct Point

{

int x, y;

}point[N];

 

int pre[N], vis[N], id[N];

double in[N];

 

double zhuliu (int root, int n, int m)

{

double res = 0;

int u, v;

while (1)

{

for (int i = 0; i < n; ++i)

{

in[i] = (double)inf;

}

for (int i = 0; i < m; ++i)

{

if (edge[i].u != edge[i].v && edge[i].w < in[edge[i].v])

{

in[edge[i].v] = edge[i].w;

pre[edge[i].v] = edge[i].u;

}

}

for (int i = 0; i < n; ++i)

{

if (i != root && in[i] == inf)

{

return -1;

}

}

int tn = 0;

memset (id, -1, sizeof (id));

memset (vis, -1, sizeof(vis));

in[root] = 0;

for (int i = 0; i < n; ++i)

{

res += in[i];

v = i;

while (vis[v] != i && id[v] == -1 && v != root)

{

vis[v] = i;

v = pre[v];

}

if (v != root && id[v] == -1)

{

for (int u = pre[v]; u != v; u = pre[u])

{

id[u] = tn;

}

id[v] = tn++;

}

}

if (tn == 0)

{

break;

}

for (int i = 0; i < n; ++i)

{

if (id[i] == -1)

{

id[i] = tn++;

}

}

for (int i = 0; i < m; ++i)

{

v = edge[i].v;

edge[i].u = id[edge[i].u];

edge[i].v = id[edge[i].v];

if (edge[i].u != edge[i].v)

{

edge[i].w -= in[v];

}

}

n = tn;

root = id[root];

}

return res;

}

 

double dist (Point a, Point b)

{

int x = a.x - b.x;

int y = a.y - b.y;

return sqrt (double (x * x + y * y));

}

 

int main ()

{

int n, m;

int u, v;

while (~scanf("%d%d", &n, &m))

{

for (int i = 0; i < n; ++i)

{

scanf("%d%d", &point[i].x, &point[i].y);

}

for (int i = 0; i < m; ++i)

{

scanf("%d%d", &u, &v);

--u;

--v;

edge[i].u = u;

edge[i].v = v;

edge[i].w = dist (point[u], point[v]);

}

double ans = zhuliu (0, n, m);

if (ans == -1)

{

printf("poor snoopy\n");

}

else

{

printf("%.2f\n", ans);

}

}

return 0;

}

哈希表

const int HASH = 1000007;

struct HASHMAP

{

    int head[HASH],next[MAXN],size;

    long long state[MAXN];

    void init()

    {

        size = 0;

        memset(head,-1,sizeof(head));

    }

    bool check(long long val){

        int h = (val%HASH+HASH)%HASH;

        for(int i = head[h];i != -1;i = next[i])

            if(val == state[i])

                return true;

        return false;

    }

    int insert(long long val)

    {

        int h = (val%HASH+HASH)%HASH;

        for(int i = head[h]; i != -1;i = next[i])

            if(val == state[i])

            {

                return 1;

            }

        state[size] = val;

        next[size] = head[h];

        head[h] = size++;

        return 0;

    }

} H1,H2;

阶层的逆元,线性求逆元

( n! )的逆元 * n  == ( (n-1)! )的逆元

long long INV(long long a,long long m)//求逆元

{

    if(a==1)

        return 1;

    return INV(m%a,m)*(m-m/a)%m;

}

const int maxn=200010;

int f[maxn],rf[maxn];//f阶层,rf阶层的逆元

 

long long C(int n,int m)//组合数

{

    return (long long )f[n]*rf[m]%mod*rf[n-m]%mod;

}

f[0]=1;

for(int i=1;i<=maxn-10;i++)

   f[i]=(long long)f[i-1]*i%mod;

    

   // ( n! )的逆元 * n  == ( (n-1)! )的逆元

rf[maxn-10]=INV(f[maxn-10],mod);

for(int i=maxn-10;i>=1;i--)

   rf[i-1]=(long long)rf[i]*i%mod;

 

线性求逆元

inv[1]=1;

    for(int i=2;i<maxn;i++)

        inv[i]=(mod-(mod/i)*inv[mod%i]%mod)%mod;

 

n个球,m个盒子问题(公式解决)

N球,M盒,由于球是否相同,盒是否相同,盒是否可以为空,共2^3=8种:

1、球同,盒同,盒不可以为空  PmN--这符号表示部分数为mN-分拆的个数,mP的下标,为了好看我将大写的M弄成小写

2、球同,盒同,盒可以为空    PmN+M--为什么要加M,与4为什么要在3的基础上加M是一样的,就是为了保证不为空

3、球同,盒不同,盒不可以为空  C(N-1, M-1)

4、球同,盒不同,盒可以为空   C(N+M-1, M-1)

5、球不同,盒同,盒不可以为空  S(N, M) --第二类斯特林数

6、球不同,盒同,盒可以为空   S (N, 1) + S(N, 2) + S(N, 3) + ... + S(N, M)

7、球不同,盒不同,盒不可以为空  M! * S(N, M)

8、球不同,盒不同,盒可以为空 M^N  --表示MN次方

积分公式题目

给圆柱外面裹一层厚度为d的巧克力,求体积和表面积。

 

快速读入(正负都可读)

inline int read()

{

    char ch=getchar();int x=0,f=1;

    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}

    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}

    return x*f;

}

一些常数

1-n之间有多少个素数       最大素数

101次方        4
102次方        25
103次方        168        997 
104次方        1229       9973    19997 
105次方        9592       49999
106次方        78498       
107次方        664579
108次方        5761455
109次方        50847534

 

unsigned   int   04294967295   

int   21474836482147483647 

unsigned long 04294967295

long   21474836482147483647

long long的最大值:9223372036854775807

long long的最小值:-9223372036854775808

unsigned long long的最大值:18446744073709551615

 

__int64的最大值:9223372036854775807

__int64的最小值:-9223372036854775808

unsigned __int64的最大值:18446744073709551615

 

 

1+1/2+1/3+......+1/n ≈ lnn+C(C=0.57722......一个无理数,称作欧拉初始,专为调和级数所用)n很大时,有:1+1/2+1/3+1/4+1/5+1/6+...1/n = 0.57721566490153286060651209 + ln(n)//C++里面用log(n)pascal里面用ln(n)  

0.57721566490153286060651209叫做欧拉常数  

 

memset(match, -1, sizeof(*match) * n);

memset(visited, 0, sizeof(*visited) * n);

 

2的次方

1 2

2 4

3 8

4 16

5 32

6 64

7 128

8 256

9 512

10 1024

11 2048

12 4096

13 8192

14 16384

15 32768

16 65536

17 131072

18 262144

19 524288

20 1048576

21 2097152

22 4194304

23 8388608

24 16777216

25 33554432

26 67108864

27 134217728

28 268435456

29 536870912

30 1073741824

31 2147483648

32 4294967296

33 8589934592

34 17179869184

35 34359738368

36 68719476736

37 137438953472

38 274877906944

39 549755813888

40 1099511627776

41 2199023255552

42 4398046511104

43 8796093022208

44 17592186044416

45 35184372088832

46 70368744177664

47 140737488355328

48 281474976710656

49 562949953421312

50 1125899906842624

 

0 0
原创粉丝点击