acm算法模板(4)
杂乱小模板
状态压缩dp小技巧
x&-x是取x的最后一个1的位置。
x-=x&-x是去掉x的最后一个1。
读入外挂
int nxt_int(){// neg or pos char ch; int flag = 0, tmp = 0; for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar()) if (ch == int('-')) break; if (ch == int('-')) flag = 1; else tmp = int(ch) - '0'; for (ch = getchar(); '0' <= ch && ch <= '9'; ch = getchar()) tmp = tmp * 10 + int(ch) - 48; return (flag) ? -tmp : tmp;}
关于N个小球放M个盒子解答
n个球放入m个箱子里,有多少种不同的放法(不一定是球和箱子,也可能是其他的元素与其他的放置位置,例如N个人分到M个单位,每班至少一人,里面已经暗中说明球不同,单位不同)
看似很简单的问题其实非常复杂,球是否相同,箱是否相同?是否允许有空盒
不难看出一共8类情况
1)球同,盒同,无空箱
2)球同,盒同,允许空箱
3)球同,盒不同,无空箱
4)球同,盒不同,允许空箱
5)球不同,盒相同,无空箱
6)球不同,盒相同,允许空箱
7)球不同,盒不同,无空箱
6)球不同,盒不同,允许空箱
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
先来看3,4.这个就是最典型的公考中经常遇见的插板法(关于插板法的解释我懒的说了,自己搜,论坛百度都容易找的到)
只是需要注意是否允许空箱
3的公式是把n个球排成一排,(一种方法),它们中间有n-1个空。取m-1个小棍,放到空上,就把它们分成m部分,由于小棍不相邻,所以没有空箱子。它的方法数有C(N-1,M-1),也就是球减1里面挑M-1个箱子做组合
4的公式在3的基础上升华出来的,为了避免空箱子,先在每一个箱子假装都放一个球,这样就有n+m个球,C(n+m-1,m-1),多了M个元素而已
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
关于1,2类情况,本来我想教大家一个特殊三角形的,但画起来比较麻烦,速度还不如穷举快,所以就略了,愿意学的我还是可以教他,不会真的还不如穷举来的快。个人建议还是用最常见的凑数法,而且公考中不会出现球和盒子数字比较大的情况。
法,例如7个相同球放入4个相同盒子,每盒至少一个(1号情况),则先4个盒子每个放1个,多余3个。只需要考虑这3个球的去处就OK,由于盒子相同,所以只需要凑数就OK,不必考虑位置。
比如300,211,111只有三种
例如7个相同球放入4个相同盒子,可以空盒,则还是凑数,大的化小的,小的化更小的。。。。。。
0,0,0,7
0,0,1,6
0,0,2,5
0,0,3,4
0,1,1,5
0,1,2,4
0,1,3,3
0,2,2,3
1,1,1,4
1,1,2,3
1,2,2,2
11种
1,2,3,4公考常见类型,必须学会!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1234都是球相同的情况。但如果球不同怎么办???
先来分析最特殊的8号:N球不同,M箱不同,允许空。每个球都有M种选择,N个球就有M的N次方分法。
关于5,6,7这情况,自己推咯,
看起来很复杂,其实很简单
第一左右两边都是1,第几行就有几个数,比如第5行就是1XXX1
第2 S(n,k)=S(n-1,k-1)+k*S(n-1,k),含义是第N排的第K个数等于他上一排的上一个位置数字加上一排的同样位置数字的K倍
例如S(7,3)就是第7排第3个数字,所以他等于上排第6排第2个数字+第6排第3个位置*3
所以画图的话,明显第1排是1,第2排1,1,推理第3排(左右两边都是1,只有中间那个数字没确定)
所以S(3,2)=第2排第1个数字+第2排第2个数字两倍=1+1*2=3,所以第3排数字就是1,3,1.同理S(4,2)=S(3,1)+2*S(3,2)=1+2*3=7,
S(4,3)=S(3,2)+3*S(3,3)=3+3*1=6......如此类推三角形
-----------------------------------------------------------------------------------------
当遇见类型5即:N不同球,M同箱子,无空箱。一共有S(N,M)种分法,比如7个不同球,4个相同箱子,每个箱子至少一个,则看三角形的第7行,第4个数字多少。
而类型6,N不同球,M同箱,允许空的时候(在类型5的基础上允许空箱)。明显是N个球不变,一个空箱子都没有+有一个空箱子+有两个空箱子+有三个空箱子+,,,,,,都装在一个箱子。说的简单点一共有就是
S(N,1)+S(N,2)+S(N,3)+..........S(N,M)=也就是说第N排开始第1个数字一直加到第M个数字就是总的分法
----------------------------------------------------------------------------------------
而类型7同样是在类型5的基础上升华,因为5是箱同的,而7箱不同,所以箱子自身多了P(M,M)=M!倍可能
所以类型7的公式就是M!乘以S(N,M)
/***************************筛法打素数表*************************/
/*
**函数作用是打不超过n的所有素数
**注意n不能超过上限 会数组越界和超时
**返回值是素数表中素数个数 _prime储存素数
**_prime一定是数组的指针
*/
int print_prime(int n,int *_prime)
{
bool sign[200000];
memset(sign,0,sizeof(sign));
sign[1]=true;//对于认为1是素数的题我们可以把这句话注释掉
int i,j,k;
for(i=4;i<=n;i+=2)
sign[i]=true;
for(i=3;i<=n;i+=2)
{
if(!sign[i])
{
k=2*i;
for(j=i*i;j<=n;j+=k)
sign[j]=true;
}
}
memset(_prime,0,sizeof(_prime));
int num=0;
for(i=1;i<=n;i++)
if(!sign[i])
_prime[num++]=i;
return num;
}
二分求幂
/*
**二分求幂的函数 注意long long有时候要用__int64
**传值的时候可以强制性转换
*/
long long half_n(long long x,long long n,long long mod)
{
if(x==0)
return 0;
if(x==1||n==0)
return 1;
long long temp,ans;
temp=x;
ans=1;
while(n)
{
if(n&1)
ans=ans*temp%mod;
temp=temp*temp%mod;
n=n>>1;
}
return ans;
}
判断是不是质数
bool is_prime(int x)
{
int i;
if(x>2&&x%2==0)
return false;
for(i=3;i*i<x;i+=2)
if(x%i==0)
return false;
return true;
}
/***********************最大公约数和最小公倍数********************/
long long gcd(long long a,long long b)
{
long long temp;
while(b)
{
temp=b;
b=a%b;
a=temp;
}
return a;
}
long long lcm(long long a,long long b)
{
return a /gcd(a,b) *b;
}
/*********************格式化输入输出*********************************/
a 符号 作用
——————————————————————————
%d 十进制有符号整数
%u 十进制无符号整数
%f 浮点数
%s 字符串
%c 单个字符
%p 指针的值
%e 指数形式的浮点数
%x, %X 无符号以十六进制表示的整数
%o 无符号以八进制表示的整数
%g 自动选择合适的表示法
━━━━━━━━━━━━━━━━━━━━━━━━━━
说明:
(1). 可以在"%"和字母之间插进数字表示最大场宽。
例如: %3d 表示输出3位整型数, 不够3位右对齐。
%9.2f 表示输出场宽为9的浮点数, 其中小数位为2, 整数位为6,
小数点占一位, 不够9位右对齐。
%8s 表示输出8个字符的字符串, 不够8个字符右对齐。
如果字符串的长度、或整型数位数超过说明的场宽, 将按其实际长度输出。
但对浮点数, 若整数部分位数超过了说明的整数位宽度, 将按实际整数位输出;
若小数部分位数超过了说明的小数位宽度, 则按说明的宽度以四舍五入输出。
另外, 若想在输出值前加一些0, 就应在场宽项前加个0。
例如: %04d 表示在输出一个小于4位的数值时, 将在前面补0使其总宽度
为4位。
如果用浮点数表示字符或整型量的输出格式, 小数点后的数字代表最大宽度,
小数点前的数字代表最小宽度。
例如: %6.9s 表示显示一个长度不小于6且不大于9的字符串。若大于9, 则
第9个字符以后的内容将被删除。
(2). 可以在"%"和字母之间加小写字母l, 表示输出的是长型数。
例如: %ld 表示输出long整数
%lf 表示输出double浮点数
(3). 可以控制输出左对齐或右对齐, 即在"%"和字母之间加入一个"-" 号可
说明输出为左对齐, 否则为右对齐。
例如: %-7d 表示输出7位整数左对齐
%-10s 表示输出10个字符左对齐
2. 一些特殊规定字符
━━━━━━━━━━━━━━━━━━━━━━━━━━
/*************************立方之和和******************************/
1^3+2^3+.....+n^3=n^2(n+1)^2/4=[n(n+1)/2]^2
/****************************平方之和***************************************/
1/6*n*(n+1)*(2*n+1);
/*****************************k次方之和********************************/
/**************************完全数*******************************-/
1……6
2……28
3……496
4……8128
5……33550336
6……8589869056
7……137438691328
8……2305843008139952128
9……2658455991569831744654692615953842176
10……191561942608236107294793378084303638130997321548169216
11……13164036458569648337239753460458722910223472318386943117783728128
12……14474011154664524427946373126085988481573677491474835889066354349131199152128
/**************************************////////////
/*******************//////////////////////////////
/********************梅森素数****************************/
|
n |
Mn |
Mn的位数 |
|
1 |
2 |
3 |
1 |
|
2 |
3 |
7 |
1 |
|
3 |
5 |
31 |
2 |
|
4 |
7 |
127 |
3 |
|
5 |
13 |
8191 |
4 |
|
6 |
17 |
131071 |
6 |
|
7 |
19 |
524287 |
6 |
|
8 |
31 |
2147483647 |
10 |
|
9 |
61 |
2305843009213693951 |
19 |
|
10 |
89 |
618970019…449562111 |
27 |
|
11 |
107 |
162259276…010288127 |
33 |
|
12 |
127 |
170141183…884105727 |
39 |
|
13 |
521 |
686479766…115057151 |
157 |
|
14 |
607 |
531137992…031728127 |
183 |
|
15 |
1,279 |
104079321…168729087 |
386 |
|
16 |
2,203 |
147597991…697771007 |
664 |
|
17 |
2,281 |
446087557…132836351 |
687 |
|
18 |
3,217 |
259117086…909315071 |
969 |
|
19 |
4,253 |
190797007…350484991 |
1,281 |
|
20 |
4,423 |
285542542…608580607 |
1,332 |
|
21 |
9,689 |
478220278…225754111 |
2,917 |
|
22 |
9,941 |
346088282…789463551 |
2,993 |
|
23 |
11,213 |
281411201…696392191 |
3,376 |
|
24 |
19,937 |
431542479…968041471 |
6,002 |
|
25 |
21,701 |
448679166…511882751 |
6,533 |
|
26 |
23,209 |
402874115…779264511 |
6,987 |
|
27 |
44,497 |
854509824…011228671 |
13,395 |
|
28 |
86,243 |
536927995…433438207 |
25,962 |
|
29 |
110,503 |
521928313…465515007 |
33,265 |
|
30 |
132,049 |
512740276…730061311 |
39,751 |
|
31 |
216,091 |
746093103…815528447 |
65,050 |
|
32 |
756,839 |
174135906…544677887 |
227,832 |
|
33 |
859,433 |
129498125…500142591 |
258,716 |
|
34 |
1,257,787 |
412245773…089366527 |
378,632 |
|
35 |
1,398,269 |
814717564…451315711 |
420,921 |
|
36 |
2,976,221 |
623340076…729201151 |
895,932 |
|
37 |
3,021,377 |
127411683…024694271 |
909,526 |
|
38 |
6,972,593 |
437075744…924193791 |
2,098,960 |
|
39 |
13,466,917 |
924947738…256259071 |
4,053,946 |
|
40* |
20,996,011 |
125976895…855682047 |
6,320,430 |
|
41* |
24,036,583 |
299410429…733969407 |
7,235,733 |
|
42* |
25,964,951 |
122164630…577077247 |
7,816,230 |
|
43* |
30,402,457 |
315416475…652943871 |
9,152,052 |
|
44* |
32,582,657 |
124575026…053967871 |
9,808,358 |
|
45* |
37,156,667 |
202254406…308220927 |
11,185,272 |
|
46* |
42,643,801 |
169873516…562314751 |
12,837,064 |
|
47* |
43,112,609 |
316470269…697152511 |
12,978,189 |
/*****************************KMP***********************************/
void getnext(char str[],int next[])
{
int len=strlen(str);
int i=0;next[i]=-1;
int j=-1;
while(i<len)
{
if(j==-1||str[i]==str[j])
{
i++;j++;
next[i]=j;
}
else
j=next[j];
}
}
int KMP(char str1[],char str2[],int pos,int next[])
{
int len1=strlen(str1);
int len2=strlen(str2);
int i,j;
i=pos;j=0;
while(i<len1&&j<len2)
{
if(j==-1||str1[i]==str2[j])
{
i++;j++;
}
else
j=next[j];
}
if(j==len2)
return i-len2;
return -1;
}
/////////////////////树状数组///////////////////
Code
int lowbit(int x)//计算lowbit
{
return x&(-x);
}
void add(int i,int val)//将第i个元素更改为val
{
while(i<=n)
{
c[i]+=val;
i+=lowbit(i);
}
}
int sum(int i)//求前i项和
{
int s=0;
while(i>0)
{
s+=c[i];
i-=lowbit(i);
}
return s;
}
/***************** 关于分割平面的探讨************************/
1.直线(Line)分割平面
由于第n条直线与前n-1条直线相交于n-1个点,这n-1个点将第n条直线划分为n个部分,而这第n条直线的两边分别有L(n-1)和n个部分。故L(n)=L(n-1)+n L(0) = 1
2.一次折线(Zig)分割平面
由于一条一次折线相对于两条直线相交少了两个部分,所以Z(n) = L(2n) - 2n Z(0) = 2
3.Z型折线(Zig-zag)分割平面
Z型线与三条之间很相似,但是有两条平行,这样就少了1个部分,如果再将有两条直线平行的三条直线变为Z型的话,则又将少4个部分,这样每个Z型折线比三条直线分平面形成的部分少5个。故ZZ(n) = L(3n)-5n
P.S.对于2,3而言由于后面的图形与前面的图形相交时要获得尽量多的区间,所以交点不会在顶点处,既然不在顶点处,那么本来这个图形相比直线而言少了几个区间,与其他图形相交后仍然少那几个区间
4.圆(Circle)相交
第n个圆与前n-1个圆相交于2(n-1)个点,这2(n-1)个点将第n个圆划分为2(n-1)个圆弧,这每条弧都将原来的一个旧区域一分为二。故C(n) = C(n-1) + 2(n-1) C(0) = 1
5.平面(Flat)分割空间
第n个平面与前n-1个平面有n-1条交线,这n-1条交线分割第n个平面为L(n-1)个区域,则在这个平面的两侧分别把空间分为F(n-1)和L(n-1)个区域,故F(n) = F(n-1) + L(n-1)
6.对没有三条对角线交于一点的凸多边形,计算各边和对角线组成的互不重叠的区域个数
我们从各区域的顶点总数和所有区域的内角和的总和两个角度进行分析:
设:Nk为区域中k边形的个数。
角度1:各区域顶点总数(包括重复计算的数目)的等式为
3N3 + 4N4 + ... + mNm = 4C(n,4) + n(n-2) (等式1)
其中m是各区域边数的最大值,n是凸多边形的顶点数。
左式表示的各区域顶点总数来自两个方面:
由于每两条对角线(或四个顶点)决定一个内部区域的顶点,因此区域的顶点数是4C(n,4),即每个内部顶点在左式中计数4次(总是四个区域公共一个顶点).又因为在计算中,凸多边形的每个顶点(为n-2个三角型的公共顶点)重复计数n-2次,因此左式的计算中,n个顶点被计数为n(n-2).
角度2:所有区域的内角和的总和的等式为
180N1 + 360N2 + 540N3 + ... + (m-2)180Nm = C(n,4)360 + (n-2)180 (等式2)
左式表示的所有区域的内角和的总和来自两个方面:
(1)各内部顶点处区域内角和(360)的总和为C(n,4)360
(2)凸多边形的内角和为(n-2)180
等式2两边同时除以180,得出
N3 + 2N4 + ... + (m-2)Nm = 2C(n,4) + (n-2) (等式3)
殊途同归,由等式1两边同时减去等式3两边,可得出区域总数:
N3 + N4 + N4 + ... Nm = C(n,4) + C(n-1,2)
这就是说,所求的区域总数为C(n,4) + C(n-1,2)
7.n条直线划分矩形
每条直线和矩形必然交于两点,这两点任一个都必然同时属于两个多边形.矩形内部n条直线两两相交且没有3条交于同一点,所以产生n(n-1)/2个交点,每个交点属于4个多边形,所以矩形边上和内部的点数一共为 n(n-1)/2+2n=n(n+3)/2.如果尽量让直线与矩形相交的时候交点不是矩形的顶点的话还会多出来4个顶点,每个顶点只属于一个多边形,所以总的点数为n(n+3)/2+4 = (n^2+3n+8)/2.
内部每个交点处内角和为360度,边上每个交点内角和为180度,顶点处内角和为90度,4个加起来是360度,仿照5的方法列方程可得到区域总数为(n^2+n+2)/2
8.圆圈上n个点两两连线最多把圆分成多少个区域
先把每个点和与它相邻的两个点相连,这样就转化成凸多边形的问题了,求出凸多边形内部的区域数,再加上n就行了.
/***********************************************************************/