CODEVS1056 圆内三角形统计
题目描述:圆周上有N(N<=100)个点,用线段将它们彼此相连。这些线段中任意三条在圆内都没有公共交点,问这些线段能构成多少个顶点在圆内的三角形?
思路:c(n,6).每一个园内三角形的三边都是圆上不同的点,所以就是从n个点中取6个的组合数。
#include<iostream> #include<cstdio> using namespace std; int main() { long long ans,n,i; cin>>n; ans=1; for (i=n-5;i<=n;++i) ans=ans*i; ans=ans/720; cout<<ans<<endl; }
CODEVS1172 Hankson的趣味题
1. x 和a0 的最大公约数是a1;
2. x 和b0 的最小公倍数是b1。
Hankson 的“逆问题”就是求出满足条件的正整数x。但稍加思索之后,他发现这样的
x 并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的x 的个数。请你帮
助他编程求解这个问题。
思路:分解质因数,然后就是美丽的乘法原理了,思路不是很难,但是有了思路之后苦苦看了。。。近两个小时,疯了。。。先对最小公倍数分解质因数,将质因数和个数存于结构体中,然后对其他的三个数进行分解,将质因数和最小公倍数的结构体下标相对应(方便后面计算)。根据唯一分解定理,可以将一个数分解成多个素数乘积的形式,而两个数的最大公约数就是两个数同底数较小指数的幂的乘积;最小公倍数就是两个数同底数较大指数的幂的乘积。然后对每一个底数的指数的范围进行求解,分为以下几种情况:
1)b1<a1,直接输出0;
2)a1<a0&&b1>b0&&b1>a1, 直接输出0;
3)a1<a0,则指数只能取a1,continue;
4)b1>b0,则指数只能取b1,continue;
5)一般情况:范围是(a1~b1)。
ans=(max-min)*(max-min)*……。
一开始用的数组下标代表质因数,数组中的是个数,然后RC、TLE了,后来借鉴了网上的解析,改成了结构体,并发现了最小公倍数的质因数一定是四个数中最全的。还有就是质因数的分解,i从2~int(sqrt(b1))每次都将i除尽,然后能整除的就一定是质数了。
#include<cstdio> #include<iostream> #include<cstring> #include<cmath> using namespace std; int ai0[10000][2]={0},ai1[10000][2]={0},bi0[10000][2]={0}, bi1[10000][2]={0},minn[10000]={0},maxn[10000]={0}; int main() { int n,a0,a1,b0,b1,ci,i,j,tt; long long ans; cin>>n; for (ci=1;ci<=n;++ci) { cin>>a0>>a1>>b0>>b1; memset(bi1,0,sizeof(bi1)); for (i=2;i<=int(sqrt(b1));++i) { if (b1%i==0) { ++bi1[0][0]; bi1[bi1[0][0]][0]=i; } while (b1%i==0) { ++bi1[bi1[0][0]][1]; b1=b1/i; } } if (b1!=1) { ++bi1[0][0]; bi1[bi1[0][0]][0]=b1; bi1[bi1[0][0]][1]=1; } memset(ai1,0,sizeof(ai1)); for (i=1;i<=bi1[0][0];++i) { tt=0; while (a1%bi1[i][0]==0) { ++tt; a1=a1/bi1[i][0]; } ++ai1[0][0]; ai1[ai1[0][0]][0]=bi1[i][0]; ai1[ai1[0][0]][1]=tt; } memset(bi0,0,sizeof(bi0)); for (i=1;i<=bi1[0][0];++i) { tt=0; while (b0%bi1[i][0]==0) { ++tt; b0=b0/bi1[i][0]; } ++bi0[0][0]; bi0[bi0[0][0]][0]=bi0[0][0]; bi0[bi0[0][0]][1]=tt; } memset(ai0,0,sizeof(ai0)); for (i=1;i<=bi1[0][0];++i) { tt=0; while (a0%bi1[i][0]==0) { ++tt; a0=a0/bi1[i][0]; } ++ai0[0][0]; ai0[ai0[0][0]][0]=ai0[0][0]; ai0[ai0[0][0]][1]=tt; } ans=1; for (i=1;i<=bi1[0][0];++i) { if (bi1[i][1]<ai1[i][1]) { ans=0;break;} if(ai1[i][1]<ai0[i][1]&&bi1[i][1]>bi0[i][1]&&bi1[i][1]>ai1[i][1]) {ans=0;break;} if(ai1[i][1]<ai0[i][1]) {minn[i]=ai1[i][1];maxn[i]=ai1[i][1];continue;} if(bi1[i][1]>bi0[i][1]) {minn[i]=bi1[i][1];maxn[i]=bi1[i][1];continue;} minn[i]=ai1[i][1]; maxn[i]=bi1[i][1]; } if (ans==1) for (i=1;i<=bi1[0][0];++i) ans=ans*(maxn[i]-minn[i]+1); cout<<ans<<endl; } }
CODEVS11572^k进制数
设r是个2k 进制数,并满足以下条件:
(1)r至少是个2位的2k 进制数。
(2)作为2k 进制数,除最后一位外,r的每一位严格小于它右边相邻的那一位。
(3)将r转换为2进制数q后,则q的总位数不超过w。
在这里,正整数k(1≤k≤9)和w(k<W< span>≤30000)是事先给定的。
问:满足上述条件的不同的r共有多少个?
我们再从另一角度作些解释:设S是长度为w 的01字符串(即字符串S由w个“0”或“1”组成),S对应于上述条件(3)中的q。将S从右起划分为若干个长度为k 的段,每段对应一位2k进制的数,如果S至少可分成2段,则S所对应的二进制数又可以转换为上述的2k 进制数r。
例:设k=3,w=7。则r是个八进制数(23=8)。由于w=7,长度为7的01字符串按3位一段分,可分为3段(即1,3,3,左边第一段只有一个二进制位),则满足条件的八进制数有:
2位数:高位为1:6个(即12,13,14,15,16,17),高位为2:5个,…,高位为6:1个(即67)。共6+5+…+1=21个。
3位数:高位只能是1,第2位为2:5个(即123,124,125,126,127),第2位为3:4个,…,第2位为6:1个(即167)。共5+4+…+1=15个。
所以,满足要求的r共有36个。
思路:自己模拟了一下样例,一个矩阵,然后就明白了些什么,但是第一次做的时候从0到n循环,每次都要做一个从i+1到n的累加,造成了TLE。后来从n到0循环,每次都只加一次,就过了(神奇的codevs告诉我RC*10)。高精度加法。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; struct use{ int l,num[300]; }f[2][31000],ans; struct use jia(struct use a,struct use b) { int i,j,g=0; a.l=max(a.l,b.l); for (i=1;i<=a.l;++i) { a.num[i]=a.num[i]+b.num[i]+g; g=a.num[i]/10; a.num[i]%=10; } while (g>0) { ++a.l; a.num[a.l]=g; g/=10; } return a; } int main() { int k,w,i,j,n,ma,nn,lun,p,start; memset(ans.num,0,sizeof(ans.num)); ans.l=0; for (p=0;p<=n;++p) { memset(f[0][p].num,0,sizeof(f[0][p].num)); f[0][p].l=0; } cin>>k>>w; n=2; for (i=2;i<=k;++i) n*=2; --n; lun=(w-1)/k+1; if (lun<2) cout<<0<<endl; else { ma=(w-1)%k+1; nn=2; for (i=2;i<=ma;++i) nn*=2; --nn; for (i=0;i<=n;++i) { f[0][i].l=1; f[0][i].num[1]=1; } for (i=2;i<lun;++i) { for (p=0;p<=n;++p) { memset(f[1][p].num,0,sizeof(f[1][p].num)); f[1][p].l=0; } f[1][n]=f[0][n+1]; for (p=n-1;p>=0;--p) f[1][p]=jia(f[1][p+1],f[0][p+1]); if (i>2) ans=jia(ans,f[1][0]); for (p=0;p<=n;++p) f[0][p]=f[1][p]; } if (lun>2) start=0; else start=1; for (p=0;p<=n;++p) { memset(f[1][p].num,0,sizeof(f[1][p].num)); f[1][p].l=0; } f[1][n]=f[0][n+1]; if (n<=nn) ans=jia(ans,f[1][n]); for (i=n-1;i>=start;--i) { f[1][i]=jia(f[1][i+1],f[0][i+1]); if (i<=nn) ans=jia(ans,f[1][i]); } for (i=ans.l;i>=1;--i) cout<<ans.num[i]; cout<<endl; } }
CODEVS1135选择客栈
丽江河边有 n 家很有特色的客栈,客栈按照其位置顺序从1 到n 编号。每家客栈都按照某一种色调进行装饰(总共k 种,用整数0 ~ k-1 表示),且每家客栈都设有一家咖啡店,每家咖啡店均有各自的最低消费。
两位游客一起去丽江旅游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定分别住在色调相同的两家客栈中。晚上,他们打算选择一家咖啡店喝咖啡,要求咖啡店位于两人住的两家客栈之间(包括他们住的客栈),且咖啡店的最低消费不超过p。
他们想知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费不超过p元的咖啡店小聚。
思路:正难则反,结果就是所有的数减去不符合的数,注意每次的操作都是对同一种颜色进行的,最后c(n,2)(n个里面取2个的组合)=n*(n-1)/2。
#include<iostream> #include<cstdio> using namespace std; int sum[51]={0},summ[51]={0}; long long ans=0,agans=0; int main() { int n,k,p,i,j,co,va; cin>>n>>k>>p; for (i=1;i<=n;++i) { cin>>co>>va; ++co; ++sum[co]; if (va<=p) for (j=1;j<=k;++j) { agans=agans+(summ[j]*(summ[j]-1))/2; summ[j]=0; } else ++summ[co]; } for (i=1;i<=k;++i) { ans=ans+(sum[i]*(sum[i]-1))/2; agans=agans+(summ[i]*(summ[i]-1))/2; } cout<<ans-agans<<endl; }
bzoj1041 圆上的整点
题目大意:求a^2+b^2=r^2中的整数对个数。
思路:有一个最简勾股数的公式:a=m^2-n^2,b=2*m*n,c=m^2+n^2(m,n为正整数),有了这个公式之后,我们穷举出所有r的约数x,然后暴力一下1~sqrt(x/r)中满足条件的m、n(其中a、b互换的情况只能求出一个来),最后×8+4就可以了。
这个公式的证明如下:a^2+b^2=c^2 <==> (a/c)^2+(b/c)^2=1(一) <==> u^2=(1-v)(1+v) <==> u/(1+v)=(1-v)/u=k <==> v=(1-k^2)/(k^2+1),u=2k/(k^2+1),将u、v回代入(一)式,可得。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define maxnode 100005 #define LL long long using namespace std; int prime[maxnode]={0},su[maxnode][2]={0},cnt=0; LL ans=0,r; bool flag[maxnode]={false}; void shai(int n) { int i,j; for (i=2;i<=n;++i) { if (!flag[i]) prime[++prime[0]]=i; for (j=1;j<=prime[0]&&i*prime[j]<=n;++j) { flag[i*prime[j]]=true; if (i%prime[j]==0) break; } } } int gcd(int a,int b){return b==0 ? a : gcd(b,a%b);} void calc(int n) { int i,j,x,y,up;up=sqrt(n-1); for (i=1;i<=up;++i) { j=sqrt(n-i*i); if (j*j!=n-i*i||i<=j) continue; x=i*i-j*j;y=2*i*j; if (gcd(n,gcd(x,y))==1) ++ans; } } void dfs(int i,int ji) { LL mi;int j; if (i>cnt) { calc((int)(r/ji));return; } mi=1; for (j=0;j<=su[i][1];++j) { dfs(i+1,ji*mi);mi*=(LL)su[i][0]; } } int main() { freopen("cir.in","r",stdin); freopen("cir.out","w",stdout); int i,j,l; scanf("%I64d",&r); shai(sqrt(r));l=(int)r; for (i=1;i<=prime[0];++i) if (l%prime[i]==0) { su[++cnt][0]=prime[i]; while(l%prime[i]==0) { ++su[cnt][1];l/=prime[i]; } } if (l>1){su[++cnt][0]=l;su[cnt][1]=1;} dfs(1,1);printf("%I64d\n",ans*8+4); fclose(stdin); fclose(stdout); }
bzoj4544 椭圆上的整点
题目大意:求a^2+3*b^2=c^2的整数解个数。
思路:类比上一题,发现可以有a=3m^2-n^2,b=2mn,c=3m^2+n^2,c是奇数的时候这个是满足的;但c是偶数的时候(1、1、2统计不到),发现原来公式的左边都变成2a、2b、2c就可以了,判断互质的时候应该使gcd=2。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define LL long long using namespace std; LL ans; LL gcd(LL a,LL b){return (!b ? a : gcd(b,a%b));} LL ab(LL a){return (a<0LL ? -a : a);} void calc(LL c){ LL i,j,m,n,a,b; if (c&1LL){ for (m=1LL;(j=m*m*3LL)<=c;++m){ n=sqrt(c-j); if (j+n*n!=c) continue; a=ab(j-n*n);b=2LL*m*n; if (gcd(c,gcd(a,b))==1LL) ++ans; } }else{ c<<=1LL; for (m=1LL;(j=m*m*3LL)<=c;++m){ n=sqrt(c-j); if (j+n*n!=c) continue; a=ab(j-n*n);b=2LL*m*n; if (gcd(c,gcd(a,b))==2LL) ++ans; } } } int main(){ LL i,n;int t; scanf("%d",&t); while(t--){ scanf("%I64d",&n); for (ans=0LL,i=1LL;i*i<=n;++i){ if (n%i) continue; calc(i); if (i*i!=n) calc(n/i); }printf("%I64d\n",ans*4LL+2LL); } }
更详细的:click here!
bzoj1965 洗牌
题目大意:给出n张牌,平分两堆后,取上面一堆的第一张、下面一堆的第二张、上面一堆的第二张……问洗m次牌后第l张牌是什么。
思路:从上一步x到下一步2x%(n+1),m步之后就是2^mx%(n+1)==l,求出2^m在模(n+1)的逆元,就可以了。(当然也可以求出2的逆元=n/2+1)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; LL n,m,p,l; LL cheng(LL a,LL b) { LL c; if (b==0) return 0; if (b==1) return a%p; c=cheng(a,b/2); if (b%2==0) return (c+c)%p; else return ((c+c)%p+a)%p; } LL mi(LL a,LL b) { LL c; if (b==0) return 1; if (b==1) return a%p; c=mi(a,b/2); if (b%2==0) return cheng(c,c); else return cheng(cheng(c,c),a); } void gcd(LL a,LL b,LL &d,LL &x,LL &y) { if (!b){d=a;x=1;y=0;} else{gcd(b,a%b,d,y,x);y-=x*(a/b);} } LL ni(LL a) { LL d,x,y; gcd(a,p,d,x,y); return (x+p)%p; } int main() { scanf("%I64d%I64d%I64d",&n,&m,&l);p=n+1; printf("%I64d\n",cheng(l,ni(mi(2,m)))); }
bzoj1406 密码箱
题目大意:求x^2=1(mod n)(x=1~n)
思路:化简一下有(x+1)(x-1)=0(mod n),要求满足i|(x+1),j|(x-1),i*j=n或者i|(x-1),j|(x+1),i*j=n,我们就分别枚举一下i,j中小的那一个,求一下相应的x,用map维护去重就可以了。(注意x每次加i,j大的那一个,看和小的那一个的关系就可以了。)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<cmath> #define maxnode 1000005 using namespace std; map<int,int> hash; int yue[maxnode]={0}; int main() { int n,i,j,x;scanf("%d",&n); if (n==1) printf("None\n"); else { int up=sqrt(n); for (i=1;i<=up;++i) if (n%i==0) yue[++yue[0]]=i; hash.clear(); for (i=1;i<=yue[0];++i) { j=n/yue[i]; for (x=1;x<n;x+=j) if ((x+1)%yue[i]==0) hash[x]=1; for (x=j-1;x<n;x+=j) if ((x-1)%yue[i]==0) hash[x]=1; } map<int,int>::iterator it; for (it=hash.begin();it!=hash.end();++it) printf("%d\n",it->first); } }
这道题目也可以对n分解质因数,之后发现每个质因数相应取1或者pi^ai-1(2的时候可能有4种情况:1,2^k,2^(k-1)+1,2^(k-1)-1),然后dfs出所有的情况,中国剩余定理合并一下就可以了。
bzoj2956 模积和
题目大意:求sigma(i=1~n,j=1~m,i!=j)(n%i)×(m%j)。
思路:如果没有i!=j的条件就是很容易的calc(n)×calc(m)(calc(x)表示sigma(i=1~x)(x%i),这个可以用sqrt(n)的时间求出,因为对于商一样的情况,余数是等差数列),然后就是i=j的情况,sigma(i=1~min(n,m))(n%i)*(m%i)=sigma(i=1~min(n,m))(n-[n/i](下取整)*i)(m-[m/i]*i),拆开后发现是可以sqrt(min(n,m))求解的形式,但要注意这里面的每一个区间都是[n/i][m/i]分别一样的区间,但这两个值本身可能并不相等,注意!!!
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long #define p 19940417LL using namespace std; LL ni; void gcd(LL a,LL b,LL d,LL &x,LL &y) { if (!b){d=a;x=1;y=0;} else{gcd(b,a%b,d,y,x);y-=x*(a/b);} } LL calc(int x) { int i,last;LL ans=0; for (i=1;i<=x;i=last+1) { last=x/(x/i); ans=(ans+(LL)(x%last+x%i)*(LL)(last-i+1)/2%p)%p; } return ans; } LL jian(LL x,LL y){return ((x-y)%p+p)%p;} LL jia(LL x,LL y){return (x+y)%p;} LL ff(LL x){return (x*(x+1)%p)*((2*x+1)*ni%p)%p;} LL cc(LL x,LL y) { int i,last;LL ans; if (x>y) swap(x,y); ans=(x*x%p)*y%p; for (i=1;i<=x;i=last+1) { last=min(x/(x/i),y/(y/i)); ans=jian(ans,((x*(y/i)+y*(x/i))%p)*((LL)(i+last)*(LL)(last-i+1)/2%p)%p); ans=jia(ans,((x/i)*(y/i)%p)*jian(ff((LL)last),ff((LL)(i-1)))%p); } return ans; } int main() { int n,m;LL d,y; scanf("%d%d",&n,&m);gcd((LL)6,p,d,ni,y);ni=(ni%p+p)%p; printf("%I64d\n",jian(calc(n)*calc(m)%p,cc((LL)n,(LL)m))); }
bzoj1225 求正整数
题目大意:求有n个约数的最小正整数m。
思路:一开始想的dp,f[i][j]表示用前i个质数有j个因数的最小数,但是会发现如果n取一个很大的质数,那最小的应该是2^(n-1),已经一万多位了,如果高精度的话会暴内存,后来才明白原来可以存log(m)(!!!)而不是m,这样就会很小了。这道题目可以暴搜,因为后面一个取得次方一定小于前面的,加上最优化减枝,就可以过了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define maxnode 50005 #define LL long long #define inf 1e20 using namespace std; int prime[17]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53}; int ans[maxnode]={0},ci[20]={0},cc[20]={0}; double lo[17],minn; void cheng(int x) { int i,j=0; for (i=1;i<=ans[0];++i) { ans[i]=ans[i]*x+j; j=ans[i]/10;ans[i]%=10; } while(j){ans[++ans[0]]=j%10;j/=10;} } void dfs(int i,int lev,int la,double num) { int j; if (num>=minn) return; if (i==17) { if (lev==1) { for (j=1;j<=16;++j) ci[j]=cc[j]; minn=num; } return; } for (j=1;j*j<=lev&&j<=la;++j) if (lev%j==0) { if (j*j!=lev) { cc[i]=j-1;dfs(i+1,lev/j,j,num+lo[i]*(j-1)); } cc[i]=lev/j-1;dfs(i+1,j,lev/j,num+lo[i]*(lev/j-1)); } } int main() { int n,i,j,t; for (i=1;i<=16;++i) lo[i]=log(prime[i])*1.0/log(2); scanf("%d",&n);minn=inf*1.0; dfs(1,n,n,0.0);ans[0]=ans[1]=1; for (i=1;i<=16;++i) for (j=1;j<=ci[i];++j) cheng(prime[i]); for (i=ans[0];i>=1;--i) printf("%d",ans[i]); printf("\n"); }
bzoj3629 聪明的燕姿
题目大意:求约数和为s的所有的数。
思路:约数和公式是sigma(prime[i]|x)sigma(j=0~prime[i]^j|x)prime[i]^j。所以筛出所有的质数,然后dfs一下,如果有大于根x的约数,就要特判一下素数。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxm 100000 #define LL long long #define inf 2100000000LL using namespace std; int prime[maxm]={0},ci[maxm]={0},tot; LL ans[maxm]={0}; bool flag[maxm]={false}; void shai(int n){ int i,j;LL x; for (i=2;i<n;++i){ if (!flag[i]) prime[++prime[0]]=i; for (j=1;j<=prime[0]&&prime[j]*i<n;++j){ flag[i*prime[j]]=true; if (i%prime[j]==0) break; } } } bool pri(LL x){ for (LL i=2;i*i<=x;++i) if (x%i==0) return false; return true; } void dfs(int last,LL le,LL ci){ if (le==1){ans[++tot]=ci;return;} if (le>prime[last+1]&&pri(le-1)) ans[++tot]=ci*(le-1); int j;LL x,tt; for (j=last+1;prime[j]*prime[j]<=le&&j<=prime[0];++j){ x=prime[j]+1;tt=prime[j]; for (;x<=le;tt*=(LL)prime[j],x+=tt) if (le%x==0) dfs(j,le/x,ci*tt); } } int main(){ int i,j;LL s; shai(maxm); while(scanf("%I64d",&s)==1){ tot=0;dfs(0,s,1LL);sort(ans+1,ans+tot+1); printf("%d\n",tot); for (i=1;i<tot;++i) printf("%I64d ",ans[i]); if (tot) printf("%I64d\n",ans[tot]); } }
bzoj3643 phi的反函数
题目大意:给定n,求phi=n的最小的x,无解或者答案超过int输出-1。
思路:因为phi的求法中的数,所以可以dfs剩下的数能整除那个prime-1,在穷举prime的幂,最后如果剩下的是1则更新答案,如果剩下的数是素数表里没有筛出来的数要判断一下。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define maxm 1000005 #define inf (1<<31)-1 #define LL long long using namespace std; int prime[maxm]={0}; LL ans=inf,n;bool f=false; bool flag[maxm]={false}; void shai(int n){ int i,j; for (i=2;i<=n;++i){ if (!flag[i]) prime[++prime[0]]=i; for (j=1;j<=prime[0]&&prime[j]*i<=n;++j){ flag[prime[j]*i]=true; if (i%prime[j]==0) break; } } } bool judge(LL x){ for (LL i=2;i*i<=x;++i) if (x%i==0) return false; return true; } void dfs(int i,LL ci,LL le){ if (le==1){ans=min(ans,ci);f=true;return;} if (judge(le+1)){ans=min(ans,ci*(le+1LL));f=true;} int k,j;LL x,y; for (k=i+1;k<=prime[0]&&prime[k]*prime[k]<=le;++k){ if (le%(prime[k]-1)==0){ x=le/(prime[k]-1);y=1; while(x%y==0){ dfs(k,ci*y*(LL)prime[k],x/y); y*=(LL)prime[k]; } } } } int main(){ int i,j;scanf("%I64d",&n);shai(sqrt(n)); dfs(0,1LL,n);printf("%I64d\n",!f?-1:ans); }
bzoj4305 数列的GCD
题目大意:给定一个数列ai,1<=ai<=m,求每一个d(1<=d<=m),长度为n的数列bi,使得gcd(bi)=d且1<=bi<=m且ai、bi恰有k个不同元素的bi的个数。
思路:对于一个d,ansd=(m/d)^(cid)*C(n-cid,k-cid)*(m/d-1)^(k-cid)-sigma(i=2*d~m/d*d)ansi。对于n/1+n/2+...+n/n=nlogn,所以就可以做出来了。注意0!的逆元是1。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxm 300005 #define LL long long #define p 1000000007LL using namespace std; int cnt[maxm]={0},ai[maxm]={0},ci[maxm]={0}; LL jie[maxm],ans[maxm],ni[maxm]; LL mi(LL x,int y){ if (y==0) return 1LL; if (y==1) return x%p; LL mm=mi(x,y/2); if (y%2) return mm*mm%p*x%p; else return mm*mm%p; } LL C(int n,int m){return jie[n]*ni[m]%p*ni[n-m]%p;} int main(){ int n,m,i,j,k;scanf("%d%d%d",&n,&m,&k); for (i=1;i<=n;++i){scanf("%d",&ai[i]);++cnt[ai[i]];} jie[0]=1LL;ni[0]=1LL; for (i=1;i<=n;++i){jie[i]=jie[i-1]*(LL)i%p;ni[i]=mi(jie[i],p-2);} for (i=1;i<=m;++i) for (j=i;j<=m;j+=i) ci[i]+=cnt[j]; for (i=1;i<=m;++i) ci[i]=n-ci[i]; for (i=m;i;--i){ if (ci[i]>k) ans[i]=0; else{ ans[i]=mi((LL)m/i,ci[i]); ans[i]=ans[i]*C(n-ci[i],k-ci[i])%p*mi((LL)(m/i-1),k-ci[i])%p; for (j=i*2;j<=m;j+=i) ans[i]=((ans[i]-ans[j])%p+p)%p; } }for (i=1;i<m;++i) printf("%I64d ",ans[i]); printf("%I64d\n",ans[m]); }
bzoj1951 古代猪文
题目大意:求g^(sigma i|n c(n,i))%p.(p=999911659)
思路:要使用超级幂,所以要求sigma i|n c(n,i) %(p-1)的值,可以根n枚举n的约数,然后计算c(n,i)%(p-1),因为p-1=2*3*4679*35617,所以可以分别求出c(n,i)%pr[i]的值,然后中国剩余定理合并一下,算出答案。
注意:要能快速准确写出lucas定理,gcd,快速幂,中国剩余定理。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define up 4 #define N 40000 #define LL long long #define p 999911659LL using namespace std; int pr[up]={2,3,4679,35617}; LL ci[up]={0LL},fac[4][N]={0LL}; LL mi(LL x,LL y,LL pp){ if (!y) return 1LL; if (y==1) return x%pp; LL mm=mi(x,y/2,pp); if (y%2) return mm*mm%pp*x%pp; else return mm*mm%pp;} void gcd(LL a,LL b,LL &x,LL &y){ if (!b){x=1LL;y=0LL;} else{gcd(b,a%b,y,x);y-=a/b*x;} } LL lucas(int n,int m,int pi){ LL ans=1;int a,b,pp;pp=pr[pi]; while(n&&m){ a=n%pp;b=m%pp; if (a<b) return 0; ans=ans*fac[pi][a]%pp*mi(fac[pi][b]*fac[pi][a-b]%pp,pp-2,pp)%pp; n/=pp;m/=pp; }return ans;} LL calc(int n){ int i,j;LL m=1LL,ans=0LL,x,y; for (i=1;i*i<=n;++i){ if (n%i) continue; for (j=0;j<up;++j) ci[j]=(ci[j]+lucas(n,i,j))%pr[j]; if (i*i!=n) for (j=0;j<up;++j) ci[j]=(ci[j]+lucas(n,n/i,j))%pr[j]; }for (i=0;i<up;++i) m*=pr[i]; for (i=0;i<up;++i){ gcd(m/pr[i],pr[i],x,y); ans=(ans+ci[i]*(m/pr[i])%(p-1)*x%(p-1))%(p-1); }return (ans%(p-1)+(p-1))%(p-1);} int main(){ LL g,n;int i,j; for (i=0;i<up;++i) for (fac[i][0]=1LL,j=1;j<pr[i];++j) fac[i][j]=fac[i][j-1]*j%pr[i]; scanf("%d%I64d",&n,&g); if (g==p) printf("0\n"); else printf("%I64d\n",mi(g%p,calc(n),p)); }
bzoj1407 Savage
题目大意:有n个人,已知他们起始位置、每次位置变化、变化次数,求使n个人中任意两人不会相遇的最小的位置数。
思路:因为答案<=10^6,所以枚举答案,然后两个两个的求最小相遇的位置,如果能在两人寿命内相遇,就不可行。
注意:扩展欧几里德中新的方程的每一项都/gcd(a,b),所以求最小解的时候%的数也是/gcd(a,b)的。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 20 #define LL long long using namespace std; struct use{LL c,p,l;}ai[N]; int n; void gcd(LL a,LL b,LL &d,LL &x,LL &y){ if (!b){d=a;x=1LL;y=0LL;} else{gcd(b,a%b,d,y,x);y-=a/b*x;}} bool judge(LL m){ int i,j;LL a,b,c,d,x,y,mm; for (i=1;i<=n;++i) for (j=i+1;j<=n;++j){ a=ai[i].p-ai[j].p;b=m; c=ai[j].c-ai[i].c; a=(a%m+m)%m;c=(c%m+m%m); if (!c) return false; gcd(a,b,d,x,y); if (c%d) continue; x=x*(c/d);mm=m/d;x=(x%mm+mm)%mm; if (x<=ai[i].l&&x<=ai[j].l) return false; }return true; } int main(){ LL m=0;int i;scanf("%d",&n); for (i=1;i<=n;++i){ scanf("%I64d%I64d%I64d",&ai[i].c,&ai[i].p,&ai[i].l); m=max(m,ai[i].c); }for (;;++m) if (judge(m)) break; printf("%I64d\n",m); }
bzoj3142 数列
题目大意:有k个数,数列单增且都是正数,相邻两个数的差不超过M,最大数不超过N,问方案数。
思路:设i+1和i的差是xi,z=x1+...+xn-1,gz表示这样的方案数,对于z的方案对答案的贡献是gz*(N-z)。N*gz=N*M^(k-1),考虑xi取t的时候对答案的贡献,x*(x的出现次数)=x*M^(k-2)*(k-1)(!!!),x取1~M,所以答案就是N*M^(k-1)-M*(M+1)/2*M^(k-2)*(k-1)。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define LL long long using namespace std; LL n,m,k,p; LL mi(LL x,LL y){ LL a=1LL;if (y<0) return 0LL; for (;y;y>>=1LL){ if (y&1LL) a=a*x%p; x=x*x%p; }return a;} int main(){ scanf("%I64d%I64d%I64d%I64d",&n,&k,&m,&p); LL ans=((n%p*mi(m,k-1LL)%p-m*(m+1LL)/2%p*mi(m,k-2LL)%p*(k-1LL)%p)%p+p)%p; printf("%I64d\n",ans); }
bzoj3157&&3516&&4126
bzoj2328 赛车游戏
题目大意:已知参数a、b,最大速度vx,油量f。n段曲线,已知每一段的横纵坐标变化量。对于一段曲线的耗油量是si*max(0,a*vi+b*ki)(si表示这一段的距离,ki表示这一段的斜率)。求油量f一定时通过n段曲线的最小时间,如果不能通过输出IMPOSSIBLE。
思路:可以通过已知的参数求出每一段最小的速度(在ki<0的时候会有一个非零的最小速度,注意和vx取min,不会对f产生影响;对于ki>0,可以在f里减去相应的耗油量,使式子中没有b*ki的项),这样处理完如果f<0就是IMPOSSIBLE。对于同样的油量变化量df,速度变化量dv=df/(si*a),时间变化量dt=si/vi-si/(vi+dv)=df/(a*vi^2+df*vi/si),可以发现dt随vi递减,所以贪心从小的vi开始向上变(!!),对于相同的vi可以把si合并,看作一段(因为已经去掉了b*ki的影响),每次vi变到vi+1,如果f不够就break。最后算出时间。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define N 10005 #define LD double #define eps 1e-9 #define inf 1e9 using namespace std; int cmp(LD x,LD y){ if (x-y>eps) return 1 ; if (y-x>eps) return -1; return 0;} LD sqr(LD x){return x*x;} struct use{ LD si,vi; bool operator<(const use&x)const{return (cmp(vi,x.vi)<0);} }ai[N],bi[N]; int main(){ int t,i,j,k,n,bz;LD ans,xi,yi,ki,a,b,vx,f; scanf("%d",&t); while(t--){ scanf("%lf%lf%lf%lf%d",&a,&b,&vx,&f,&n); for (i=1;i<=n;++i){ scanf("%lf%lf",&xi,&yi); xi/=1000.;yi/=1000.;ki=yi/xi; ai[i].si=sqrt(sqr(xi)+sqr(yi)); ai[i].vi=(cmp(ki,0.)>=0 ? 0. : min(vx,-b*ki/a)); if (cmp(ki,0.)>=0) f-=b*ki*ai[i].si; }if (cmp(f,0.)<0){printf("IMPOSSIBLE\n");continue;} sort(ai+1,ai+n+1); for (bz=0,i=1;i<=n;i=j+1){ for (j=i;j<n&&cmp(ai[j].vi,ai[j+1].vi)==0;++j); bi[++bz]=ai[i]; for (k=i+1;k<=j;++k) bi[bz].si+=ai[k].si; }if (cmp(bi[bz].vi,vx)<0) bi[++bz]=(use){0.,vx}; for (ans=0.,i=1;i<bz;++i){ if (cmp(bi[i].si*a*(bi[i+1].vi-bi[i].vi),f)<=0){ bi[i+1].si+=bi[i].si; f-=bi[i].si*a*(bi[i+1].vi-bi[i].vi); }else{ bi[i].vi+=f/a/bi[i].si; break; } }for (;i<=bz;++i) ans+=bi[i].si/bi[i].vi; printf("%.5f\n",ans); } }
Fibonacci数列
bzoj2173 整数的lqp拆分
题目大意:自然数拆分之后,如a1+a2+...+am=n,给答案的贡献是fa1+fa2+...+fam,求所有方案的总答案。
思路:找规律,g[n]=2g[n-1]+g[n-2]。
当然也有正确的推导:g[n]=sigma(i=1~n-1)g[n-i]*f[i]+f[n]
=sigma(i=2~n-1)g[n-i](f[i-1]+f[i-2])+g[n-1]+f[n-1]+f[n-2]
=(sigma(i=1~n-2)g[n-i-1]f[i]+f[n-1])+(sigma(i=1~n-3)g[n-i-2]f[i]+f[n-2])+g[n-1]
=2g[n-1]+g[n-2]
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define p 1000000007 using namespace std; int fi[1000005]={0}; int main() { int n,i,j;scanf("%d",&n); fi[1]=1;fi[2]=2; for (i=3;i<=n;++i) fi[i]=(fi[i-1]*2%p+fi[i-2])%p; printf("%d\n",fi[n]); }
bzoj2660 最多的方案
题目大意:求把一个数用不同的Fibonacci数分解的不同方案。
思路:先把这个数分解成尽量靠右的Fibonacci数的和,因为fi[i]=fi[i-1]+fi[i-2],所以如果把选那些Fibonacci数压成01串的话,001和110的和是一样的,所以我们考虑一个10000001的串,它的和也是10000110、10011010、11101010的和,我们会发现多出来的这三种情况正好是这个1和上个1之间长度的一半。设dp[i][0/1]表示第i个贪心出来的Fibonacci数的下标选或者不选,那么dp[i][1]=dp[i-1][0]+dp[i-1][1],dp[i][0]=(ff[i]-ff[i-1])/2*dp[i-1][0]+(ff[i]-ff[i-1]-1)/2*dp[i-1][1],最后答案就是dp[][0]+dp[][1]。(注意Fibonacci数列问题的求解大多都要回归的递推式上!!!)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; LL fi[200]={0},dp[200][2]={0}; int ff[200]={0}; int main(){ int i,j;LL n;scanf("%I64d",&n); fi[1]=1;fi[2]=2; for (fi[0]=3;fi[fi[0]-1]<=n;++fi[0]) fi[fi[0]]=fi[fi[0]-1]+fi[fi[0]-2]; --fi[0];for (i=fi[0];i;--i) if (n>=fi[i]){ff[++ff[0]]=i;n-=fi[i];} for (i=1;i<=ff[0]/2;++i) swap(ff[i],ff[ff[0]+1-i]); dp[0][1]=1;j=ff[0];ff[0]=0; for (i=1;i<=j;++i){ dp[i][1]=dp[i-1][0]+dp[i-1][1]; dp[i][0]=(ff[i]-ff[i-1])/2*dp[i-1][0]+(ff[i]-ff[i-1]-1)/2*dp[i-1][1]; }printf("%I64d\n",dp[j][0]+dp[j][1]); }
省队集训R2 day6 T1(!!!)
题目大意:已知n,x,v。其中i个同时合并的代价是i*x+v,问n个合并成一个的最小代价。
思路:暴力的话fi[i]=min(fi[i/j(上取整)]+j*x+v),其中i/j的值只有√n个,所以是O(n√n)的。每次分的时候一定是分成的j份尽量均等,如果看做一个k叉树,每层的ki一定是[y,y+1]中的,其中∏ki>=n。代价是sigma ki*x,高度是h的时候,还会贡献代价v*(h-1)。所以可以枚举高度,然后二分一个最小的满足的y,计算答案。看哪些是y哪些是y+1的时候,可以先用y^(h-1),然后不够的话/y*(y+1),同时对代价进行修改。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long #define up 60 #define ur 100000000 using namespace std; LL n; bool judge(int x,LL y){ LL ci;int i; for (ci=1LL,i=1;i<=x;++i){ ci*=y; if (ci>=n) return true; if (i<x&&(n+ci-1)/ci<=y) return true; }return false;} int main(){ freopen("apples.in","r",stdin); freopen("apples.out","w",stdout); int i,j,l,r,mid;LL ans,ci,cc,x,v; while(scanf("%I64d%I64d%I64d",&n,&x,&v)==3){ ans=n*x+v; for (i=2;i<=up;++i){ l=1;r=ur; while(l<=r){ mid=(l+r)/2; if (judge(i,mid)) r=mid-1; else l=mid+1; }for (cc=1LL,j=1;j<=i;++j) cc*=r; ci=r*i*x; for (;cc<n;){cc=cc/r*l;ci+=x;} ans=min(ans,ci+v*i); }printf("%I64d\n",ans); } }
排列组合
bzoj3505 数三角形
题目大意:n*m网格中的格点三角形数量。
思路:所有的方案就是C(n*m,3),不符合的方案就是所有共线的三角形组数,我们可以穷举两个端点,中间的每一个点都可以构成,一个向量上格点数是gcd(i,j)-1,因为这个向量可以平移,所以我们给这个数*(n-i)*(m-j),又因为可以朝上或者朝下,所以*2。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; LL c(int x) { if (x<=2) return 0; return (LL)x*(LL)(x-1)/2*(LL)(x-2)/3; } int gcd(int a,int b){return a%b==0 ? b : gcd(b,a%b);} int main() { int n,m,i,j,x,y;LL ans; scanf("%d%d",&n,&m);++n;++m; ans=c(n*m)-(LL)m*c(n)-(LL)n*c(m); for (i=1;i<n;++i) for (j=1;j<m;++j) ans-=2*(gcd(i,j)-1)*(LL)(n-i)*(LL)(m-j); printf("%I64d\n",ans); }
bzoj1211树的计数||1005 明明的烦恼
题目大意:给定每个点的度数,求可能的树的个数。(明明的烦恼中有的点没有度数要求)
思路:prufer数列+组合。prufer数列是一个n-2的数列,对应唯一的一棵树,生成这个数列的方法是:取叶子节点中编号最小的,把它连的点加入数列并把这个叶子节点从树中删掉;有数列还原为树的方法是在数列最后加一个n,然后从前往后扫,每次取数列中未出现的最小数,与数列中相应位置连边。有了这n-2个位置,我们可以对一个度数为di的点选出di-1个位置,所以答案应该是c(n-2,d1-1)*c(n-2-d1+1,d2-1)...,化简一下会发现很多都约掉了,是(n-2)!/(d1-1)!/(d2-1)!/...,可以用分解质因数的方法求出答案,防止一些问题。这样树的计数这道题目就a了。但是明明的烦恼中有一些点没有度数限制,我们可以先把有度数限制的那些用前面的思路做出来(注意如果用化简的式子,会和上面的略有不同!!!),剩下的n-2-sum(度数没限制的点的个数)个位置每个位置都可任意取sum种数,所以*sum^(n-2-sum)就可以了。注意两道题中都要判断无解的情况:如果所有有度数限制的点的度数-1之和!=n-2就是无解,无度数限制的时候注意一下就可以了。
注意数组的大小,开太大可能没法运行或者会tle。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxnode 1005 #define LL long long using namespace std; int ai[maxnode]={0},prime[maxnode]={0},cn[maxnode]={0}; bool flag[maxnode]={false}; struct use{LL num[10000];}ans; use cheng(use a,LL b) { int i,j=0; for (i=1;i<=a.num[0];++i) { a.num[i]=a.num[i]*b+j; j=a.num[i]/10;a.num[i]%=10; } while(j){a.num[++a.num[0]]=j%10;j/=10;} return a; } void shai(int n) { int i,j; for (i=2;i<=n;++i) { if (!flag[i]) prime[++prime[0]]=i; for (j=1;j<=prime[0]&&prime[j]*i<=n;++j) { flag[i*prime[j]]=true; if (i%prime[j]==0) break; } } } void fen(int x,int kk) { for (int i=1;i<=prime[0];++i) if (x%prime[i]==0) while(x%prime[i]==0){x/=prime[i];cn[i]+=kk;} } void jc(int x,int kk){for (int i=1;i<=x;++i) fen(i,kk);} void get() { int i,j; for (i=1;i<=prime[0];++i) for (j=1;j<=cn[i];++j) ans=cheng(ans,(LL)prime[i]); } void print(use a) { for (int i=a.num[0];i;--i) printf("%I64d",a.num[i]); printf("\n"); } int main() { int n,i,j,cnt=0,sum=0,x=0; scanf("%d",&n);shai(n); for (i=1;i<=n;++i) { scanf("%d",&ai[i]); if (ai[i]==0&&n!=1){printf("0\n");return 0;} if (ai[i]==-1) ++x; else sum+=ai[i]-1; } if (n==1&&ai[1]<=0) printf("1\n"); else { if (sum>n-2||(x==0&&sum!=n-2)) printf("0\n"); else { ans.num[0]=ans.num[1]=1;jc(n-2,1); for (i=1;i<=n;++i) if (ai[i]!=-1) jc(ai[i]-1,-1); jc(n-2-sum,-1); if (x) fen(x,n-2-sum); get();print(ans); } } }
bzoj2111 排列计数
题目大意:求1~n的满足a[i]>a[i/2]的排列的个数。
思路:如果我们把i向i/2连边就会发现这是一个堆,我们需要取树上的节点当且仅当它没有父亲,求这样的合理的取的顺序,我们可以给堆中的点附上它们取得顺序,这样就是这么多点小根堆的个数了,可以树上dp,对于x个点的堆:f[x]=c(x-1,lsiz)*f[lsiz]*f[rsiz],最后的f[1]即为答案。这里的组合数很大,可以用lucas或者处理出阶乘之后求。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long #define maxnode 2000005 using namespace std; int siz[maxnode]={0}; LL fi[maxnode]={0}; LL mi(LL x,LL y,LL p) { if (y==0) return 1; if (y==1) return x%p; LL mm=mi(x,y/2,p); if (y%2) return mm*mm%p*x%p; else return mm*mm%p; } LL cc(int n,int m,int p) { LL zi,mu,d,x,y;zi=mu=1; if (n<m) return 0; if (n==m) return 1; if (m>n-m) m=n-m; for (int i=1;i<=m;++i){zi=zi*(n-m+i)%p;mu=mu*i%p;} return zi*mi(mu,(LL)p-2,(LL)p)%(LL)p; } LL lucas(int n,int m,int p) { LL ans=1; while(n&&m&&ans) { ans=ans*cc(n%p,m%p,p)%p; n/=p;m/=p; }return ans; } void dfs(int n,int u,int p) { int l,r;l=u<<1;r=l|1;siz[u]=1; if (l<=n){dfs(n,l,p);siz[u]+=siz[l];} if (r<=n){dfs(n,r,p);siz[u]+=siz[r];} if (l>n){fi[u]=1;return;} if (r>n){fi[u]=fi[l];return;} fi[u]=fi[l]*fi[r]%p*lucas(siz[u]-1,siz[l],p)%p; } int main() { int n,p;scanf("%d%d",&n,&p); dfs(n,1,p);printf("%lld\n",fi[1]); }
bzoj4403 序列统计
题目大意:求取值范围为l~r的长度为1~n的不下降序列的个数。
思路:长度为i的个数是c(l-r+i,i),但是这里是长度为1~n的,化简组合数的公式也比较麻烦,所以可以从意义上考虑:新加一个元素l-1,从l-1~r中选n个元素的不下降序列个数-全是l-1的那一个就是答案了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define p 1000003LL #define LL long long using namespace std; LL jie[p]; LL mi(LL x,LL y){ if (!y) return 1LL; if (y==1) return x%p; LL mm=mi(x,y/2); if (y%2) return mm*mm%p*x%p; else return mm*mm%p;} void pre(){ int i,j;jie[0]=1; for (i=1;i<p;++i) jie[i]=jie[i-1]*(LL)i%p;} LL lucas(int m,int n){ LL cnt=1LL;int a,b; while(m&&n){ a=m%p;b=n%p; if (a<b) return 0LL; cnt=cnt*jie[a]%p*mi(jie[b]*jie[a-b]%p,p-2)%p; m/=p;n/=p; }return cnt;} int main(){ int t,n,l,r;scanf("%d",&t); pre(); while(t--){ scanf("%d%d%d",&n,&l,&r); printf("%I64d\n",(lucas(n+r-l+1,n)-1LL+p)%p); } }
bzoj3193 地形生成
题目大意:给定每个编号的高度和关键值,合法的序列中,i编号前的比它高的个数小于它的关键值。求标号序列和等高线序列的个数。
思路:第一问比较简单,从高往低放,相同高度(关键值小的在前)的一起放,每个能放的有min(i,v)+j-i个位置;第二问比较复杂,已知一些线段,形如[1,v],每个小球放的位置对应一条线段,可以多个放在一个位置,问方案数,考虑dp,fi[i][j]表示到第i个球,放了前j个位置,fi[i][j]=sigma(k=1~j-1)fi[i-1][k],fi[1][1]=0,写的时候发现这个可以优化一下,fi[i]+=fi[i-1],乘给答案的cc=sigma(i=1~v[la])fi[i]。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 1005 #define p 2011 using namespace std; struct use{ int h,v; bool operator<(const use&x)const{return (h==x.h ? v<x.v : h>x.h);} }ai[N]; int fi[N]; int main(){ int i,j,k,la,n,ci,cc,up;scanf("%d",&n); for (i=1;i<=n;++i) scanf("%d%d",&ai[i].h,&ai[i].v); sort(ai+1,ai+n+1); for (ci=i=1;i<=n;i=la+1){ la=i;while(la<n&&ai[la+1].h==ai[la].h) ++la; for (j=i;j<=la;++j) ci=ci*(min(i,ai[j].v)+j-i)%p; }printf("%d ",ci); for (ci=i=1;i<=n;i=la+1){ la=i;while(la<n&&ai[la+1].h==ai[la].h) ++la; memset(fi,0,sizeof(fi)); for (fi[1]=1,j=i;j<=la;++j){ for (up=min(i,ai[j].v),k=2;k<=up;++k) fi[k]=(fi[k]+fi[k-1])%p; }for (up=min(i,ai[la].v),cc=0,j=1;j<=up;++j) cc=(cc+fi[j])%p; ci=ci*cc%p; }printf("%d\n",ci); }
bzoj2142 礼物
题目大意:有n个礼物,m个人,每人分wi个,问方案数。
思路:显然是∏c(n-sigma(j=1~i-1)wj,wi),但这里P是几个质数幂的乘积,所以组合数取模要用到中国剩余定理。对于%pi^ki,可以把阶乘表示成pi^k*s的形式。具体做法是:对于n超过P倍数的部分,暴力拆分;对于[kP+1,kP+P-1]中%p!=0的部分,乘积是一样的,算出一组,然后快速幂一下;%p!=0的部分/p之后是原来的子问题,复杂度是O(Plogn)。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 100005 #define LL long long using namespace std; int prime[N]={0},cnt[2][N]={0}; bool flag[N]={false}; LL ai[N],ci[N],gi[N],k,s,phi[N]; LL mi(LL x,LL y,LL p){ if (y==0LL) return 1LL; if (y==1LL) return x%p; LL mm=mi(x,y/2,p); if (y%2LL==1LL) return mm*mm%p*x%p; else return mm*mm%p;} void gcd(LL a,LL b,LL &d,LL &x,LL &y){ if (!b){d=a;x=1LL;y=0LL;return;} gcd(b,a%b,d,y,x);y-=a/b*x;} void shai(){ int i,j; for (i=2;i<N;++i){ if (!flag[i]) prime[++prime[0]]=i; for (j=1;j<=prime[0]&&i*prime[j]<N;++j){ flag[i*prime[j]]=true; if (i%prime[j]==0) break; } } } void getj(LL n,LL f,int pi){ LL i,j,cc=n/gi[pi]; for (i=cc*gi[pi]+1;i<=n;++i){ j=i;while(j%cnt[0][pi]==0){j/=cnt[0][pi];k+=f;} s=s*(f>0 ? j : mi(j,phi[pi]-1,gi[pi]))%gi[pi]; }if (cc){ LL cs=1LL;n=n-n%gi[pi]; for (i=1;i<gi[pi];++i){ if (i%cnt[0][pi]==0) continue; cs=cs*(f>0 ? i : mi(i,phi[pi]-1,gi[pi]))%gi[pi]; }cs=mi(cs,cc,gi[pi]); s=s*cs%gi[pi];k+=f*(n/cnt[0][pi]); getj(n/cnt[0][pi],f,pi); } } LL getc(LL n,LL m,int pi){ k=0LL;s=1LL; getj(n,1LL,pi);getj(m,-1LL,pi);getj(n-m,-1LL,pi); LL ans=mi(cnt[0][pi],k,gi[pi])*s%gi[pi]; return ans;} LL calc(LL p,int pn){ int i,j;LL x,y,d,ans=0LL; for (i=1;i<=pn;++i){ gcd(p/gi[i],gi[i],d,x,y); ci[i]=(ci[i]%gi[i]+gi[i])%gi[i]; ans=(ans+p/gi[i]*x%p*ci[i]%p)%p; }return (ans%p+p)%p;} int main(){ int m,i,j,pn=0;LL p,n,sum=0LL,x;shai(); scanf("%I64d%I64d%d",&p,&n,&m); for (i=1;i<=m;++i){scanf("%I64d",&ai[i]);sum+=ai[i];} if (sum>n) printf("Impossible\n"); else{ for (x=p,i=1;i<=prime[0];++i){ if (x%(LL)prime[i]) continue; cnt[0][++pn]=(LL)prime[i]; gi[pn]=ci[pn]=1LL; while(x%(LL)prime[i]==0){ x/=(LL)prime[i];++cnt[1][pn]; gi[pn]*=(LL)prime[i]; }phi[pn]=gi[pn]/(LL)prime[i]*(LL)(prime[i]-1); }for (sum=n,i=1;i<=m;++i){ for (j=1;j<=pn;++j) ci[j]=ci[j]*getc(sum,ai[i],j)%gi[j]; sum-=ai[i]; }printf("%I64d\n",calc(p,pn)); } }
bzoj4402 Claris的剑(!!!)
题目大意:一把剑宽度是1~M,相邻的两个元素绝对值差为1,第一个是1。本质不同的序列认为是相同高度的个数不同的序列个数。求序列个数。
思路:对于长度为n,最大为m的方案数,可以看作是向1、2、3、...、j中插入(2,1)(2,3)(3,4)...这样的对,相当于m-1个未知数和为(n-m)/2,且未知数都是非负整数的方案数,是C((n-m)/2+m-2,m-2),对于不同的n,有C(m,0)+C(m,1)+...+C(m,n)=C(m+1,n+1),所以答案是可以O(n)算出来的。但要注意上面是考虑n、m同奇偶的时候,如果不是,可以把最后一个放在最后,统计答案的时候也要考虑进去。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long #define p 1000000007LL #define N 4000005 using namespace std; LL fac[N],inv[N]; LL mi(LL x,int y){ LL a=1LL; for (;y;y>>=1){ if (y&1) a=a*x%p; x=x*x%p; }return a;} LL getc(LL n,LL m){return fac[n]*inv[m]%p*inv[n-m]%p;} int main(){ int i,up;LL ans,j,n,m; scanf("%I64d%I64d",&n,&m); m=min(n,m);up=(int)(n+m)/2+1;fac[0]=1LL; for (i=1;i<=up;++i) fac[i]=fac[i-1]*(LL)i%p; inv[up]=mi(fac[up],(int)p-2); for (i=up-1;i>=0;--i) inv[i]=inv[i+1]*(LL)(i+1)%p; ans=1LL; for (ans=1LL,j=2LL;j<=m;++j){ ans=(ans+2LL*getc((n-j)/2LL+j-1LL,j-1LL))%p; if ((n-j)%2LL==0LL) ans=((ans-getc((n-j)/2LL+j-2LL,j-2LL))%p+p)%p; }printf("%I64d\n",ans); }
bzoj3129 方程
题目大意:求x1+x2+...+xn=m的正整数解的组数,其中xi<=ai(1<=i<=n1),xi>=ai(n1+1<=i<=n2)。(1<=n1,n2<=8)
思路:对于n1+1~n2的情况,都减去ai-1就没有限制了。1~n1的情况可以用容斥,总的-1个不满足的+2个-...(不满足就是让这个某个数>ai,也就是给m-ai)。然后就是求组合数,因为P是pi^xi的形式,所以用礼物一题的方法,但是可以通过预处理1~pi^xi-1中%pi不为0的数的阶乘来加速。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 100005 #define LL long long using namespace std; LL ci[6],gi[N],p,cnt[2][N],fac[6][N]; int n,m,n1,n2; int ai[N],prime[N]={0},phi[N],pn=0; bool flag[N]={false}; struct use{LL k,s;}; LL mi(LL x,LL y,LL pp){ if (y==0LL) return 1LL; if (y==1LL) return x%pp; LL mm=mi(x,y/2,pp); if (y%2LL==1LL) return mm*mm%pp*x%pp; else return mm*mm%pp;} void gcd(LL a,LL b,LL &x,LL &y){ if (!b){x=1LL;y=0LL;return;} gcd(b,a%b,y,x);y-=a/b*x;} void shai(){ int i,j; for (i=2;i<N;++i){ if (!flag[i]) prime[++prime[0]]=i; for (j=1;j<=prime[0]&&i*prime[j]<N;++j){ flag[i*prime[j]]=true; if (i%prime[j]==0) break; } } } void pre(){ int i,j,x; for (x=p,i=1;i<=prime[0];++i){ if (x%(LL)prime[i]) continue; cnt[0][++pn]=(LL)prime[i]; gi[pn]=ci[pn]=1LL; while(x%(LL)prime[i]==0){ x/=(LL)prime[i];++cnt[1][pn]; gi[pn]*=(LL)prime[i]; }phi[pn]=gi[pn]/(LL)prime[i]*(LL)(prime[i]-1); fac[pn][0]=1LL; for (j=1;j<gi[pn];++j){ if (j%prime[i]==0) fac[pn][j]=fac[pn][j-1]; else fac[pn][j]=fac[pn][j-1]*(LL)j%gi[pn]; } } } use getj(int nn,int pi){ use a,b; if (nn<cnt[0][pi]){ a.k=0LL;a.s=fac[pi][nn]; return a; }a.k=nn/cnt[0][pi]; b=getj(nn/cnt[0][pi],pi); a.k+=b.k; a.s=b.s*fac[pi][nn%gi[pi]]%gi[pi]*mi(fac[pi][gi[pi]-1],nn/gi[pi],gi[pi])%gi[pi]; return a;} LL getc(int nn,int mm,int pi){ if (nn<0||mm<0||nn<mm) return 0LL; use a,b,c;LL ans,k; a=getj(nn,pi);b=getj(mm,pi);c=getj(nn-mm,pi); ans=a.s*mi(b.s,(LL)phi[pi]-1LL,gi[pi])%gi[pi]*mi(c.s,(LL)phi[pi]-1LL,gi[pi])%gi[pi]; k=a.k-b.k-c.k; if (k>=cnt[1][pi]) return 0; else return ans*mi(cnt[0][pi],k,gi[pi])%gi[pi]; } void dfs(int ii,int ct,int sm){ int i; if (ii>n1){ LL f=(LL)(ct%2 ? -1 : 1); for (i=1;i<=pn;++i) ci[i]=(ci[i]+f*getc(m-sm-1,n-1,i))%gi[i]; return; }if (sm+ai[ii]+n-m<=0) dfs(ii+1,ct+1,sm+ai[ii]); dfs(ii+1,ct,sm);} LL calc(){ int i;LL x,y,pm,ans=0LL;pm=p; for (i=1;i<=pn;++i){ ci[i]=(ci[i]%gi[i]+gi[i])%gi[i]; gcd(pm/gi[i],gi[i],x,y); ans=(ans+x*(pm/gi[i])%p*ci[i]%p)%p; }return (ans%p+p)%p;} int main(){ int i,x,t;scanf("%d%I64d",&t,&p); shai();pre(); while(t--){ scanf("%d%d%d%d",&n,&n1,&n2,&m); for (i=1;i<=n1;++i) scanf("%d",&ai[i]); for (i=1;i<=n2;++i){scanf("%d",&x);m-=x-1;} memset(ci,0,sizeof(ci)); dfs(1,0,0);printf("%I64d\n",calc()); } }
bzoj4591 超能粒子炮·改
题目大意:求sigma(i=0~k)c(n,k)%2333。
思路:考虑lucas定理,可以发现i/p一样的时候的i%p的sigma是相同的(最后一个sigma可能不是全的,可以单独处理),对于i/p是缩小规模的问题,所以可以递归求解。预处理c(n,i%p)的前缀和。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long #define p 2333 using namespace std; int c[p][p],sm[p][p]; int lucas(LL n,LL m){ int a,b;int ans=1; while(n&&m){ a=(int)(n%p);b=(int)(m%p); if (a<b) return 0; ans=ans*c[a][b]%p; n/=p;m/=p; }return ans;} int calc(LL n,LL k){ if (k<0LL) return 0; int ci=calc(n/p,k/p-1); return (ci*sm[(int)(n%p)][p-1]+lucas(n/p,k/p)*sm[(int)(n%p)][k%p])%p; } int main(){ int t,i,j;LL n,k; for (i=0;i<p;++i){ sm[i][0]=c[i][0]=1; for (j=1;j<=i;++j) sm[i][j]=c[i][j]=(c[i-1][j]+c[i-1][j-1])%p; }for (i=0;i<p;++i) for (j=1;j<p;++j) sm[i][j]=(sm[i][j]+sm[i][j-1])%p; scanf("%d",&t); while(t--){ scanf("%I64d%I64d",&n,&k); printf("%d\n",calc(n,k)); } }
省队集训R2 day2 T3 chaos(!!!)
题目大意:给出一个字符串,求重新排列之后(字符串不同)相同连续段数和给定串一样的个数,连续段数最多为100。
思路:统计出每个字符的个数,要求相同连续段的个数为已知的m。从前往后插入,设fi[i][j]表示前i种字符有j个连续段的个数,一共有sum[i]+1个空,其中有j+1个空是相邻连续段之间的,插入新字符的话只会给连续段+1;剩下的sum[i]-j个空是同一个连续段之内的,插入新字符会给连续段+2。转移的时候从前往后转移,枚举第i+1个字符分成的段数a和插入相邻连续段的个数b,通过组合数计算贡献。
注意:1)循环的上界最多到m;
2)因为组合数求i个字符分成j段的时候,不同的分法是不一样的,所以可以按顺序取前面的b个为插入相邻连续段的,后面a-b个为插入一个连续段内的。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 100005 #define M 105 #define up 26 #define p 1000003LL #define LL long long using namespace std; int cnt[up],sum[up+1]={0}; LL fi[2][M],fac[N],inv[N]; char ss[N]; int idx(char ch){return ch-'a';} LL mi(LL x,LL y){ LL a=1LL; for (;y;y>>=1){ if (y&1LL) a=a*x%p; x=x*x%p; }return a;} void add(LL &x,LL y){x+=y;if (x>=p) x-=p;} LL getc(int n,int m){return n<m ? 0LL : fac[n]*inv[m]%p*inv[n-m]%p;} int main(){ freopen("chaos.in","r",stdin); freopen("chaos.out","w",stdout); int n,m=0,i,j,a,b,uj,ua,ub,cur,la; LL vv;scanf("%s",ss); n=strlen(ss); for (i=0;i<n;i=j+1){ for (j=i;ss[j+1]==ss[j];++j); ++m;cnt[idx(ss[j])]+=j-i+1; }for (fac[0]=1LL,i=1;i<=n;++i) fac[i]=fac[i-1]*i%p; for (inv[n]=mi(fac[n],p-2LL),i=n-1;i>=0;--i) inv[i]=inv[i+1]*(i+1)%p; memset(fi,0,sizeof(fi)); cur=0;la=1; fi[cur][0]=1LL; for (i=0;i<up;++i){ sum[i+1]=sum[i]+cnt[i]; if (!cnt[i]) continue; cur^=1;la^=1; memset(fi[cur],0,sizeof(fi[cur])); uj=min(sum[i],m); for (j=0;j<=uj;++j){ if (!(vv=fi[la][j])) continue; ua=min(min(m,cnt[i]),sum[i]+1); for (a=1;a<=ua;++a){ ub=min(j+1,a); for (b=0;b<=ub;++b){ if (j+b+2*(a-b)>m) continue; add(fi[cur][j+b+2*(a-b)],vv*getc(cnt[i]-1,a-1)%p* getc(j+1,b)%p*getc(sum[i]-j,a-b)%p); } } } }printf("%I64d\n",fi[cur][m]); }
bzoj3294 放棋子
题目大意:给出一个nm的矩形,在其中放c种颜色,每种颜色有ci个,最后要求每行每列没有不同种颜色,求方案数。
思路:fi[k][i][j]表示前k种颜色,占i行j列的方案数;gi[k][i][j]表示k这种颜色占i行j列的方案数。fi[k][i][j]=sigma(a=1~i,b=1~j) fi[k-1][i-a][j-b]*gi[k][a][b]*C(i,a)*C(j,b),gi[k][i][j]=C(i*j,ck)-sigma((a=1~i,b=1~j)&&(a!=i||b!=j))*C(i,a)*C(j,b)。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 35 #define M 1005 #define LL long long #define p 1000000009LL using namespace std; int ci[N]; LL cc[M][M],fi[N][N][N],gi[N][N][N]; void add(LL &x,LL y){x+=y;if (x>=p) x-=p;} void minu(LL &x,LL y){x=(x+p-y)%p;} int main(){ int n,m,c,i,j,k,a,b;LL ans=0LL; scanf("%d%d%d",&n,&m,&c); for (i=1;i<=c;++i) scanf("%d",&ci[i]); memset(cc,0,sizeof(cc)); memset(fi,0,sizeof(fi)); memset(gi,0,sizeof(gi)); for (i=0;i<M;++i) for (cc[i][0]=1LL,j=1;j<=i;++j) cc[i][j]=(cc[i-1][j-1]+cc[i-1][j])%p; fi[0][0][0]=1LL; for (k=1;k<=c;++k){ for (i=1;i<=n;++i) for (j=1;j<=m;++j){ gi[k][i][j]=cc[i*j][ci[k]]; for (a=1;a<=i;++a) for (b=1;b<=j;++b){ if (a==i&&b==j) continue; minu(gi[k][i][j],gi[k][a][b]*cc[i][a]%p*cc[j][b]%p); } } for (i=1;i<=n;++i) for (j=1;j<=m;++j) for (a=1;a<=i;++a) for (b=1;b<=j;++b) add(fi[k][i][j], fi[k-1][i-a][j-b]*gi[k][a][b]%p*cc[i][a]%p*cc[j][b]%p); }for (i=1;i<=n;++i) for (j=1;j<=m;++j) add(ans,fi[c][i][j]*cc[n][i]%p*cc[m][j]%p); printf("%I64d\n",ans); }
欧拉函数及欧拉定理
bzoj3884 上帝与集合的正确用法
题目大意:求2^(2^(...))mod p的值。
思路:有一个公式:a^x=a^(x%phi(p)+phi(p)) (mod p),那么我们对于每一个指数这样化简,可以递归处理。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxnode 10000005 #define LL long long using namespace std; int prime[maxnode]={0},phi[maxnode]={0}; bool flag[maxnode]={0}; void shai(int n) { int i,j;phi[1]=1; for (i=2;i<=n;++i) { if (!flag[i]){prime[++prime[0]]=i;phi[i]=i-1;} for (j=1;j<=prime[0]&&prime[j]*i<=n;++j) { flag[prime[j]*i]=true; if (i%prime[j]) phi[i*prime[j]]=phi[i]*(prime[j]-1); else{phi[i*prime[j]]=phi[i]*prime[j];break;} } } } LL mi(LL x,LL y) { if (x==0) return 1; if (x==1) return 2%y; LL m=mi(x/2,y); if(x%2) return (m*m%y)*2%y; else return m*m%y; } LL calc(int x) { if (x==1) return 0; return mi(calc(phi[x])+phi[x],x); } int main() { int t,i,j;LL n; scanf("%d",&t);shai(10000000); while(t--) { scanf("%d",&n);printf("%I64d\n",calc(n)); } }
如果不知道这个公式也可以对于(a,p)=1的情况,a^x=a^(x%phi(p));如果(a,p)!=1,可以把p中的2都提出来,有a^x%(k*t)=k*(a^x%t),所以也可以化简。
bzoj2226 LCMsum
题目大意:求sigma(i=1~n)lcm(i,n)。
思路:sigma(i=1~n)lcm(i,n)=sigma(i=1~n)i*n/gcd(i,n)
=n*sigma(i=1~n)i/gcd(i,n)
=n*sigma(d|n)sigma(j=1~n/d)j*e(gcd(n/d,j))
=n*sigma(d|n)sigma(j=1~d)j*e(gcd(d,j))
因为小于n的与n互质的数的和为n*phi(n)/2(考虑d与n-d都与n互质,和为n)(n>=2),所以就可以根号n的求解了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxm 1000005 #define LL long long using namespace std; int prime[maxm]={0},phi[maxm]={0}; bool flag[maxm]={false}; void shai(int n){ int i,j; for (i=2;i<=n;++i){ if (!flag[i]){prime[++prime[0]]=i;phi[i]=i-1;} for (j=1;j<=prime[0]&&i*prime[j]<=n;++j){ flag[i*prime[j]]=true; if (i%prime[j]) phi[i*prime[j]]=phi[i]*(prime[j]-1); else{phi[i*prime[j]]=phi[i]*prime[j];break;} } } } int main(){ int t,n,i,j;LL ans;scanf("%d",&t); shai(1000000); while(t--){ scanf("%d",&n);ans=0LL; for (i=1;i*i<=n;++i) if (n%i==0){ ans+=(i==1 ? 1 : (LL)phi[i]*(LL)i/2LL); if (i!=n/i) ans+=(LL)phi[n/i]*(LL)(n/i)/2LL; } printf("%lld\n",ans*(LL)n); } }
bzoj3560 DZY Loves Math V
题目大意:求sigma(i1|a1)..sigma(in|an)phi(i1...in).
思路:考虑对phi(x)的x分解质因数,考虑每个质因数对答案的贡献,对于一个质因数就是(pi^0+pi^1+pi^2+...+pi^xi-1)*(p-1)/p+1(+1是因为可以不选这个质因数),这些贡献乘起来就是答案了。所以可以做个前缀和,就可以算出答案了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxm 100005 #define p 1000000007LL #define LL long long using namespace std; int prime[maxm]={0},ai[maxm]; LL sum[maxm][100]={0LL}; bool flag[maxm]={false}; void shai(int n){ int i,j; for (i=2;i<=n;++i){ if (!flag[i]) prime[++prime[0]]=i; for (j=1;j<=prime[0]&&i*prime[j]<=n;++j){ flag[i*prime[j]]=true; if (i%prime[j]==0) break; } } } LL mi(LL x,LL y){ if (y==1LL) return x%p; LL mm=mi(x,y/2LL); if (y%2LL) return mm*mm%p*x%p; else return mm*mm%p; } int main(){ int n,x,i,j;LL y,ans;shai(5000); for (i=1;i<=prime[0];++i){ sum[i][0]=1LL; for (j=1,y=(LL)prime[i];y<p;++j,y=y*(LL)prime[i]) sum[i][j]=(sum[i][j-1]+y)%p; }scanf("%d",&n);ans=1LL; for (i=1;i<=n;++i) scanf("%d",&ai[i]); for (i=1;i<=prime[0];++i){ for (y=1LL,j=1;j<=n;++j){ if (ai[j]%prime[i]) continue; x=0; while(ai[j]%prime[i]==0){ ++x;ai[j]/=prime[i]; }y=y*sum[i][x]%p; }y=(y-1LL+p)%p; if (!y) continue; ans=ans*(y*mi((LL)prime[i],p-2)%p*(prime[i]-1)%p+1LL)%p; }sort(ai+1,ai+n+1);i=1; while(i<=n&&ai[i]==1LL) ++i; for (;i<=n;i=j+1){ j=i;y=1LL; while(j<n&&ai[j+1]==ai[i]){y=y*(ai[i]+1LL)%p;++j;} y=(y*(ai[i]+1LL)%p-1LL+p)%p; ans=ans*(y*mi((LL)ai[i],p-2)%p*(ai[i]-1)%p+1LL)%p; }printf("%I64d\n",ans); }
bzoj2749 外星人
题目大意:求phi^x(n)=1的x的值(phi^x(n)=phi(phi(...phi(n))))
思路:没有思路的时候先打了个表,然后开始找规律,发现:1)一个10^5的数暴力很快就出答案;2)两个互质的数的乘积的答案就是两个数单独的和-1;3)某个质数x次方的答案是等差数列。因为题目读入的就是标准分解形式,所以相应处理就行了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define N 100000 #define M 10000 #define LL long long using namespace std; struct use{int pr,cn;}ai[N+1]={0}; int phi[N+1]={0},prime[M]={0},ci[M],po[N+1]; LL bi[M]; bool flag[N+1]={0}; int pre(int i){ int cnt=0,x=i; while(i!=1){ i=phi[i];++cnt; }return cnt;} void shai(int n){ int i,j; for (i=2;i<=n;++i){ if (!flag[i]){ prime[++prime[0]]=i;phi[i]=i-1;po[i]=prime[0]; }for (j=1;j<=prime[0]&&i*prime[j]<=n;++j){ flag[i*prime[j]]=true; if (i%prime[j]) phi[i*prime[j]]=phi[i]*(prime[j]-1); else{phi[i*prime[j]]=phi[i]*prime[j];break;} } }for (i=1;i<=prime[0];++i) ci[i]=pre(prime[i]);} int main(){ int t,n,i,j,cnt,x;LL ans,y; shai(N);scanf("%d",&t); while(t--){ ans=0LL; scanf("%d",&n); for (i=1;i<=n;++i){ scanf("%d%I64d",&x,&y);x=po[x]; ans+=(x==1 ? y : (LL)(ci[x]-1)*y+1LL); if (i>1) --ans; }printf("%I64d\n",ans); } }
bzoj2186 沙拉公主的困惑
题目大意:求1~n!中与m!互质的数的个数。T<=10000
思路:如果(x,y)=1,则(x+y,y)=1。所以答案就是phi(m!)/m!*n!=n!*∏(pi-1)/pi(pi是m!的质因数),可以线筛的时候预处理。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long #define maxnode 10000001 using namespace std; int prime[1000000]={0}; LL r,pr[maxnode]={0},fac[maxnode]={0}; bool flag[maxnode]={0}; void gcd(LL a,LL b,LL &d,LL &x,LL &y) { if (b==0){d=a;x=1;y=0;} else{gcd(b,a%b,d,y,x);y-=x*(a/b);} } void pre(LL n) { int i,j;LL x,y,d;fac[1]=pr[1]=1; for (i=2;i<=n;++i){ pr[i]=pr[i-1];fac[i]=fac[i-1]*(LL)i%r; if (!flag[i]){ prime[++prime[0]]=i; pr[i]=pr[i]*(LL)(i-1)%r; gcd(i,r,d,x,y);x=(x%r+r)%r; pr[i]=pr[i]*x%r; } for (j=1;j<=prime[0]&&(LL)i*(LL)prime[j]<=n;++j){ flag[prime[j]*i]=true; if (i%prime[j]==0) break; } } } int main() { int t;LL n,m;scanf("%d%lld",&t,&r); pre(10000000); while(t--){ scanf("%I64d%I64d",&n,&m); printf("%I64d\n",fac[n]*pr[m]%r); } }
裴蜀定理
bzoj2257 瓶子和燃料
题目大意:给定n个没刻度的瓶子和它们的容量,倒水游戏一样的规则,求用其中k个瓶子倒出最少水的最大值。
思路:裴蜀定理说ax+by=gcd(a,b),同时这个gcd(a,b)是正整数里面最小的,所以我们要求出k个数的gcd最大。我们枚举出所有数的约数,然后找到最大的出现k次的约数就是答案了。(注意这里不能简单的分解质因数,然后把出现超过某一次幂的k次的累乘,这样可能导致质因子来自不同的k个数。)
#include<cstdio> #include<algorithm> #define maxnode 33000000 using namespace std; int yue[maxnode]={0}; int main() { int n,k,i,j,x,ans=0; scanf("%d%d",&n,&k); for (i=1;i<=n;++i){ scanf("%d",&x); for (j=1;j*j<=x;++j) if (x%j==0) { yue[++yue[0]]=j; if (j*j!=x) yue[++yue[0]]=x/j; } }sort(yue+1,yue+yue[0]+1); for (i=yue[0];i;i=j-1) { j=i;while(yue[j]==yue[j-1]&&j>1) --j; if (i-j+1>=k){ans=yue[j];break;} }printf("%d\n",ans); }
不定方程组
bzoj2299 向量
题目大意:给定a,b,相当于给定向量(a,b)(a,-b)(-a,b)(-a,-b)(b,a)(b,-a)(-b,a)(-b,-a),求能否凑出(x,y)。
思路:这几个向量中本质不同的就是(a,b)(a,-b)(b,-a)(-b,-a),那么我们可以列出不等式组aA+bB+aC+bD=x,bA+aB-bC-aD=y,题目相当于问这个方程组有无整数解,而一个多元一次方程ax1+bx2+cx3+...=n有整数解的条件是(a,b,c,...)|n,所以我们要判断gcd(a,b)|x,gcd(a,b)|y,gcd(a+b,a-b)|x+y(使得两个方程有一样的解,即方程组有解)。但是对于a、b为0的一些特殊情况要特判出去。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; LL gcd(LL a,LL b){return b==0?a:gcd(b,a%b);} int main() { LL a,b,x,y,d1,d2;int t;scanf("%d",&t); while(t--) { scanf("%I64d%I64d%I64d%I64d",&a,&b,&x,&y); if (a==0&&b==0){ if (x==0&&y==0) printf("Y\n"); else printf("N\n"); continue; } if (b==0) swap(a,b); if (a==0){ if (x%b==0&&y%b==0) printf("Y\n"); else printf("N\n"); continue; }d1=gcd(a,b);d2=gcd(a+b,a-b); if (x%d1==0&&y%d1==0&&(x+y)%d2==0) printf("Y\n"); else printf("N\n"); } }
几何
bzoj2659 算不出的算式
题目大意:给定两个奇质数p,q,求sigma(i=1~(p-1)/2)iq/p+sigma(j=1~(q-1)/2)jq/p。
思路:可以把p/q看作斜率,那么就是求这条直线对应矩形的面积,因为p、q互质,所以线上没有点。如果p==q,线上就有点了,所以公式略有不同。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; LL fang(LL x){return x*x;} int main() { LL p,q;scanf("%I64d%I64d",&p,&q); printf("%I64d\n",(p==q ? (p+1)*(p-1)/4 : (p-1)/2*(q-1)/2)); }
Catalan数
如:多边形中三角形划分方案问题、出入栈问题、01序列问题、站队问题、给定二叉树点数有多少种方案数、回家问题。
bzoj2822
题目大意:用n个a*b的方块组成一个n级台阶,要求每一级都是高1宽1的,求方案数。
思路:考虑最后一层,最后一层分开几种,相应的上边就会有什么答案。h[i]=(4i-2)/(i+1)*h[i-1],h[0]=1。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define sta 10000 #define LL long long using namespace std; struct use{ int num[10000]; void init(){num[0]=num[1]=1LL;} }fi[505]={0}; use cheng(use a,int b){ use c;int i,j;LL g=0; c.num[0]=a.num[0]; for (i=1;i<=a.num[0];++i){ c.num[i]=a.num[i]*b+g;g=c.num[i]/sta;c.num[i]%=sta; }while(g){c.num[++c.num[0]]=g%sta;g/=sta;} while(c.num[c.num[0]]==0) --c.num[0]; return c; } use chu(use a,int b){ int i,j,g=0; for (i=a.num[0];i;--i){ g=g*sta+a.num[i]; a.num[i]=g/b;g=g%b; }g=0; for (i=1;i<=a.num[0];++i){ a.num[i]+=g;g=a.num[i]/sta;a.num[i]%=sta; } while(g){a.num[++a.num[0]]=g%sta;g/=sta;} while(a.num[a.num[0]]==0) --a.num[0]; return a; } void print(use a){ int i,j;printf("%d",a.num[a.num[0]]); for (i=a.num[0]-1;i;--i){ if (a.num[i]<10){printf("000%d",a.num[i]);continue;} if (a.num[i]<100){printf("00%d",a.num[i]);continue;} if (a.num[i]<1000){printf("0%d",a.num[i]);continue;} printf("%d",a.num[i]); }printf("\n"); } int main() { int n,i,j;scanf("%d",&n); fi[0].init(); for (i=1;i<=n;++i) fi[i]=chu(cheng(fi[i-1],4*i-2),i+1); print(fi[n]); }
bzoj1856 字符串
题目大意:求n个1m个0组成的前任意位中1不少于0的方案数。
思路:如果n=m的时候,就是catalan数了(还有一些变形:如从(0,0)->(n,n)不能过对角线的走法等)。考虑公式:总的方案数是c(n+m,n),然后不合理的方案一定是某一个奇数位x上为0且前面(包括这一位)的0多于1的第一个位置,那么后面就有n-(k-1)/2个1,m-(k+1)/2个0,如果把后面的0、1互换,那么总共就有n+1个0、m-1个1,并且这是一一对应,那么不符合的就是c(n+m,m-1),答案就是这两部分相减。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define p 20100403 #define LL long long using namespace std; LL mi(LL x,LL y){ if (y==1) return x%p; LL mm=mi(x,y/2); if (y%2) return mm*mm%p*x%p; else return mm*mm%p; } LL c(int n,int m){ int i;LL zi,mu;zi=1;mu=1; for (i=n-m+1;i<=n;++i) zi=zi*(LL)i%p; for (i=1;i<=m;++i) mu=mu*(LL)i%p; return zi*mi(mu,p-2)%p; } int main(){ int n,m;scanf("%d%d",&n,&m); printf("%I64d\n",((c(n+m,n)-c(n+m,m-1))%p+p)%p); }
bzoj1485 有趣的数列
题目大意:求长度为2n的1~2n的排列,满足a1<a3<...<a2n-1,a2<a4<...<a2n,a2i-1<a2i。(%p,1<=p<=1000000000)
思路:1~2n,把选在奇数位的看作0,偶数位的为1,要求前任意位0不少于1,就是经典的Catalan数了,用fi=c(2i,i)/(i-1)=∏(x=i+2~2i)x/(n!)。因为p不一定是质数,所以算每一项的时候表示成x*y(x是和p互质的数,y是p的质因子幂的乘积),这样就可以逆元,最后对p质因子的幂乘就可以了。
注意:有phi的时候最后有大质数不要忘了考虑。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long #define N 100000 using namespace std; int prime[N]={0},pr[N],pt=0,n,p,ct[N]={0},ci[N]={0},pv; LL ans; bool flag[N]={false}; void shai(){ int i,j; for (i=2;i<N;++i){ if (!flag[i]) prime[++prime[0]]=i; for (j=1;j<=prime[0]&&i*prime[j]<N;++j){ flag[i*prime[j]]=true; if (i%prime[j]==0) break; } } } LL mi(LL x,int y){ LL a=1LL; for (;y;y>>=1){ if (y&1) a=a*x%(LL)p; x=x*x%(LL)p; }return a;} void fj(int x,int y){ for (int i=1;i<=pt;++i) while(x%pr[i]==0){x/=pr[i];ci[i]+=y;} ans=ans*(y==1 ? (LL)x : mi((LL)x,pv-1))%(LL)p; } int main(){ int i,pp;shai(); scanf("%d%d",&n,&p); for (pv=pp=p,i=1;i<=prime[0];++i) if (pp%prime[i]==0){ pr[++pt]=prime[i]; while(pp%prime[i]==0){ ++ct[pt];pp/=prime[i]; }pv=pv/prime[i]*(prime[i]-1); } if (pp!=1){pr[++pt]=pp;ct[pt]=1;pv=pv/pp*(pp-1);} for (ans=1LL,i=n+2;i<=(n<<1);++i) fj(i,1); for (i=1;i<=n;++i) fj(i,-1); for (i=1;i<=pt;++i) ans=ans*mi((LL)pr[i],ci[i])%(LL)p; printf("%I64d\n",ans); }
XOR
bzoj3329 Xorequ
题目大意:解方程x^3x=2x(异或),求小于等于n的和小于等于2^n的。
思路:移项,然后就有x^2x=x+2x,那么就是所有二进制中1不相邻的数。对于第一个数位dp(还是要注意如果前面不符合了,就要break掉),第二个矩阵乘法。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define maxm 100 #define p 1000000007 #define LL long long using namespace std; struct use{LL num[3][3];}; LL fi[maxm][2]={0LL}; int xi[maxm]={0}; LL calc1(LL n){ int i,j;LL x=n,ans=0LL;xi[0]=0; while(x){xi[++xi[0]]=x%2LL;x/=2LL;} for (i=1;i<xi[0];++i) ans+=fi[i][1]; for (i=xi[0]-1;i;--i){ ans+=(LL)xi[i]*fi[i][0]; if (xi[i]&&xi[i+1]) break; }return ans; } use cheng(use m1,use m2){ use m3;int i,j,k; for (i=1;i<=2;++i) for (j=1;j<=2;++j){ m3.num[i][j]=0LL; for (k=1;k<=2;++k) m3.num[i][j]=(m3.num[i][j]+m1.num[i][k]*m2.num[k][j]%p)%p; }return m3; } use mi(use m1,LL y){ if (y==1LL) return m1; use mm=mi(m1,y/2LL); if (y%2LL) return cheng(mm,cheng(mm,m1)); else return cheng(mm,mm); } LL calc2(LL n){ use m1,mm;memset(mm.num,0,sizeof(mm.num));mm.num[1][1]=1LL; m1.num[1][1]=m1.num[1][2]=m1.num[2][1]=1LL;m1.num[2][2]=0LL; m1=cheng(mm,mi(m1,n)); return (m1.num[1][1]+m1.num[1][2])%p; } int main(){ int t,i,j;LL n;fi[0][0]=1LL; for (i=1;i<=62;++i){ fi[i][0]=fi[i-1][0]+fi[i-1][1]; fi[i][1]=fi[i-1][0]; }scanf("%d",&t); while(t--){ scanf("%I64d",&n); printf("%I64d\n%I64d\n",calc1(n+1LL),calc2(n)); } }
bzoj4260 REBXOR
题目大意:求两段不相交区间,使其异或和的和最大。
思路:如果是和的和最大,就前缀后缀一下,取max就可以了,这里是xor,所以用trie,查询的时候尽量取01不同的,前缀后缀更新一下就可以了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxm 400005 #define maxsiz 13000000 #define up 30 using namespace std; int ai[maxm]={0},pre[maxm]={0},suc[maxm]={0},fi[maxm]={0},gi[maxm]={0},xi[up+1]={0}; struct use{ int ch[maxsiz][2],sz; void init(){sz=ch[0][0]=ch[0][1]=0;} void insert(int x){ int i,j,u=0,v; memset(xi,0,sizeof(xi)); while(x){xi[++xi[0]]=x%2;x/=2;} for (i=up;i;--i){ if (!ch[u][xi[i]]){ ch[u][xi[i]]=++sz; ch[sz][0]=ch[sz][1]=0; }u=ch[u][xi[i]]; } } int ask(int x){ int i,j,u=0,ans=0; memset(xi,0,sizeof(xi)); while(x){xi[++xi[0]]=x%2;x/=2;} for (i=up;i;--i){ if (ch[u][xi[i]^1]){ ans|=1<<i-1;u=ch[u][xi[i]^1]; }else u=ch[u][xi[i]]; }return ans; } }tree; int main(){ int n,i,j,ans;scanf("%d",&n); tree.init();tree.insert(pre[0]); for (i=1;i<=n;++i){ scanf("%d",&ai[i]);pre[i]=pre[i-1]^ai[i]; fi[i]=max(fi[i-1],tree.ask(pre[i]));tree.insert(pre[i]); }tree.init();tree.insert(suc[n+1]); for (i=n;i>=1;--i){ suc[i]=suc[i+1]^ai[i]; gi[i]=max(gi[i+1],tree.ask(suc[i]));tree.insert(suc[i]); }for (i=2;i<=n;++i) ans=max(ans,fi[i-1]+gi[i]); printf("%d\n",ans); }
bzoj4017 小Q的无敌异或
题目大意:(1)求所有区间异或的和;(2)求所有区间和的异或。
思路:(1)按位考虑,统计这一位是1的有多少个区间,扫右端点r,看前缀异或和r的不同的有多少个区间;(2)(!!!)按位考虑,统计这一位是1的区间个数的奇偶,扫右端点,对于第i位,一个区间(l,r)是1的条件是(pr[r]-pr[l-1])%(2^(k+1))>=2^k,化一下式子有两种情况:(%(2^(k+1))) 1)pr[r]>pr[l],pr[r]-pr[l-1]>=2^k,即pr[l-1]<=pr[r]-2^k;(2)pr[r]<pr[l],pr[r]+(2^(k+1))-pr[l-1]>=2^k,即pr[r]<pr[l-1]<=pr[r]+2^k。
注意:(1)这题第二问的常数比较大,用线段树tle了;
(2)应用了位运算和其他运算之间的转化。一开始想的是/(2^k)%2,但是不知道怎么往下处理。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 100005 #define M 20 #define LL long long #define p 998244353LL using namespace std; inline int in(){ char ch=getchar();int x=0; while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9'){ x=x*10+ch-'0';ch=getchar(); }return x;} LL ai[N],sm[N],pr[N],tr[N],ti[N]; int n,nn; LL calc1(){ int i,j;LL ci,ans=0LL;pr[0]=0LL; for (i=1;i<=n;++i) pr[i]=pr[i-1]^ai[i]; for (i=0;i<=M;++i){ sm[0]=ci=0LL; for (j=0;j<=n;++j){ if (j) sm[j]=sm[j-1]; if ((pr[j]>>i)&1) ++sm[j]; }for (j=n;j;--j){ if ((pr[j]>>i)&1) ci+=j-sm[j-1]; else ci+=sm[j-1]; }ans=(ans+ci*(1LL<<i)%p)%p; }return ans;} inline int lowbit(int x){return x&(-x);} inline void ins(int x){for (;x<=nn;x+=lowbit(x)) tr[x]+=1LL;} inline LL ask(int x){LL sm=0LL;for (;x;x-=lowbit(x)) sm+=tr[x];return sm;} inline int getp(LL x){return upper_bound(ti+1,ti+nn+1,x)-ti-1;} LL calc2(){ int i,j,l,r;LL ans=0LL,ci,cc;pr[0]=0LL; for (i=1;i<=n;++i) pr[i]=pr[i-1]+ai[i]; for (i=0;(1LL<<i)<=pr[n];++i){ for (j=0;j<=n;++j) ti[j+1]=pr[j]%(1LL<<(i+1)); memset(tr,0,sizeof(tr)); sort(ti+1,ti+n+2); nn=unique(ti+1,ti+n+2)-ti-1; ci=0LL; for (j=0;j<=n;++j){ cc=pr[j]%(1LL<<(i+1)); ci+=ask(getp(cc-(1LL<<i)))+ask(getp(cc+(1LL<<i)))-ask(getp(cc)); ins(getp(cc)); }if (ci%2) ans|=(1LL<<i); }return ans;} int main(){ int i,j;n=in(); for (i=1;i<=n;++i) ai[i]=(LL)in(); printf("%I64d %I64d\n",calc1(),calc2()); }
二次剩余
判断有无整数满足x^2=a(mod p)。
gauss告诉我们,当p为奇质数时,如果a^((p-1)/2)=1(mod p),那么就存在解;同时如果p=2或者a=0的时候,一定有解。
三分
bzoj1857 传送带
题目大意:给定两条线段ab、cd,在ab、cd、平面上的速度分别为p、q、r,求从a到d的最少时间。
思路:在ab、cd上三分套三分,算出时间就可以了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define eps 1e-4 using namespace std; struct use{ double x,y; }a,b,c,d,e; double p,q,v; use in(){ use ci;scanf("%lf%lf",&ci.x,&ci.y); return ci; } double sqr(double x){return x*x;} double dis(use a,use b){return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));} use dian(use x,use y,double dd){ use ci;double di=dis(x,y); if (di>0.0){ ci.x=x.x+dd*(y.x-x.x)/di; ci.y=x.y+dd*(y.y-x.y)/di; return ci; }else return x; } double calc(double x,use e){return x/q+dis(e,dian(d,c,x))/v;} double judge(double x){ double l,r,m1,m2; e=dian(a,b,x);l=0.0;r=dis(c,d); while(r-l>eps){ m1=l+(r-l)/3.0; m2=r-(r-l)/3.0; if (calc(m1,e)<calc(m2,e)) r=m2; else l=m1; }return calc(l,e)+x/p; } int main(){ double l,r,m1,m2; a=in();b=in();c=in();d=in(); scanf("%lf%lf%lf",&p,&q,&v); l=0.0;r=dis(a,b); while(r-l>eps){ m1=l+(r-l)/3.0; m2=r-(r-l)/3.0; if (judge(m1)<judge(m2)) r=m2; else l=m1; }printf("%.2f\n",judge(l)); }
杜教筛
bzoj3944 Sum
题目大意:求sigma(i=1~n)phi(i)和sigma(i=1~n)mu(i)(n<2^31)
思路:phi(n)=n-sigma(d<n,d|n)phi(d),sumphi(n)=n(n+1)/2-sigma(i=2~n)simga(d|i,d<i)phi(d)=n(n+1)/2-sigma(i=2~n)sigma(d=1~n/i)phi(d)=n(n+1)/2-sigma(i=2~n)sumphi(n/i)
mu(n)=1-sigma(d<n,d|n)mu(d),summu(n)=1-sigma(i=2~n)sigma(d<i,d|i)mu(d)=1-sigma(i=2~n)sigma(d=1~n/i)mu(d)=1-sigma(i=2~n)summu(n/i)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #define up 5000000 #define LL long long using namespace std; int prime[up]={0}; LL phi[up]={0},mu[up]={0}; bool flag[up]={false}; map<int,LL>cp,cm; void shai(){ int i,j;phi[1]=mu[1]=1; for (i=2;i<up;++i){ if (!flag[i]){ prime[++prime[0]]=i; mu[i]=-1;phi[i]=i-1; }for (j=1;j<=prime[0]&&i*prime[j]<up;++j){ flag[i*prime[j]]=true; if (i%prime[j]){ phi[i*prime[j]]=phi[i]*(prime[j]-1); mu[i*prime[j]]=-mu[i]; }else{ phi[i*prime[j]]=phi[i]*prime[j]; mu[i*prime[j]]=0;break; } } }for (i=1;i<up;++i){ phi[i]+=phi[i-1];mu[i]+=mu[i-1]; }} map<int,LL>::iterator it; LL calcp(LL x){ if (x<up) return phi[x]; if ((it=cp.find(x))!=cp.end()) return it->second; LL i,j;LL sum=x*(x+1)/2; for (i=2;i<=x;i=j+1){ j=x/(x/i); sum-=(LL)(j-i+1)*calcp(x/i); }return cp[x]=sum; } LL calcm(LL x){ if (x<up) return mu[x]; if ((it=cm.find(x))!=cm.end()) return it->second; LL i,j;LL sum=1LL; for (i=2;i<=x;i=j+1){ j=x/(x/i); sum-=(LL)(j-i+1)*calcm(x/i); }return cm[x]=sum; } int main(){ int t,n;shai(); scanf("%d",&t); while(t--){ scanf("%d",&n); printf("%I64d %I64d\n",calcp((LL)n),calcm((LL)n)); } }
特征根
bzoj4002 有意义的字符串
题目大意:求((b+√d)^n)(*,下取整)%p。(0<b^2<=d<(b+1)^2<10^18,n<=10^18,b%2=1,d%4=1,)
思路:考虑(*)的共轭复数((b-√d)^n),根据数列特征根法可以知道这两个式子是a[n]=b*a[n-1]+(d-b^2)/4*a[n-2]的特征根(系数都是整数,a%2=c => a^2 %4=c),所以a[n]=((b+√d)^n)+((b-√d)^n),可以矩乘求出a[n],然后减去((b-√d)^n),因为b、d的关系,所以((b-√d)^n)只有在b!=√d且n%2=0的时候会使答案-1。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define UL unsigned long long #define N 3 #define p 7528443412579576937ULL using namespace std; UL mul(UL x,UL y){ if (!y) return 0LL; if (y==1) return x%p; UL mm=mul(x,y/2LL); if (y%2) return ((mm+mm)%p+x)%p; else return (mm+mm)%p;} struct mat{ UL x[N][N]; mat operator*(const mat&xx)const{ int i,j,k;mat y; for (i=1;i<=2;++i) for (j=1;j<=2;++j){ y.x[i][j]=0LL; for (k=1;k<=2;++k) y.x[i][j]=(y.x[i][j]+mul(x[i][k],xx.x[k][j]))%p; }return y;} }ai,bi; UL b,d; void mi(UL x){ bi.x[1][1]=2LL;bi.x[1][2]=b; for (;x;x>>=1){ if (x&1) bi=bi*ai; ai=ai*ai;}} int main(){ UL i,j,n;cin>>b>>d>>n; ai.x[1][1]=0LL;ai.x[1][2]=(d-b*b)/4LL; ai.x[2][1]=1LL;ai.x[2][2]=b; mi(n);cout<<(bi.x[1][1]-(b*b!=d && !(n%2))+p)%p<<endl; }
进制转换
转换任意进制数可以用被除数=商数×除数+余数求,余数要求非负,所以在转化-k进制数的时候,如果模数<0,可以通过商数+1,余数+k来计算。
poj The Moronic Cowmpouter
题目大意:转-2进制数。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 105 using namespace std; int ai[N]={0}; int main(){ int n;scanf("%d",&n); for (;n;){ if (n%2==-1){ ai[++ai[0]]=1; n=(-n+1)/2; }else{ai[++ai[0]]=n%2;n=-n/2;} }if (!ai[0]) ai[++ai[0]]=0; for (;ai[0];--ai[0]) printf("%d",ai[ai[0]]); }