数学

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;
} 
View Code

 

CODEVS1172 Hankson的趣味题

 题目描述 Description
已知正整数a0,a1,b0,b1,设某未知正整数x 满足:

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;

    }

} 
View Code

 

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;

    }

} 
View Code

 

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;
} 
View Code

 

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);
}
View Code

 

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);
    }
}
View Code

 更详细的: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))));
}
View Code

 

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);
    }
}
View Code

 这道题目也可以对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)));
}
View Code

 

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");
}
View Code

 

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]);
    }
}
View Code

 

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);
}
View Code

 

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]);
}
View Code

 

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));
}
View Code

 

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);
}
View Code

 

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);
}
View Code

 

bzoj3157&&3516&&4126 

click here!

 

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);
    }
}
View Code

 

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]);
}
View Code

 

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]);
}
View Code

 

省队集训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);
    }
}
View Code

 

排列组合

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);
}
View Code

 

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);
      }
    }
}
View Code

 

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]);
}
View Code

 

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);
    }
}
View Code

 

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);
}
View Code

 

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));
    }
}
View Code

 

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);
}
View Code

 

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());
    }
}
View Code

 

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));
    }
}
View Code

 

省队集训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]);
}
View Code

 

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);
}
View Code

 

欧拉函数及欧拉定理

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));
    }
}
View Code

如果不知道这个公式也可以对于(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);
    }
}
View Code

 

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);
}
View Code

 

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);
    }
}
View Code

 

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);
    }
}
View Code

 

裴蜀定理

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);
}
View Code

 

不定方程组

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");
    }
}
View Code

 

几何

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));
}
View Code

 

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]);
}
View Code

 

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);
}
View Code

 

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);
}
View Code

 

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));
    }
}
View Code

 

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);
}
View Code

 

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());
}
View Code

 

二次剩余

判断有无整数满足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));
}
View Code

 

杜教筛

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));
    }
}
View Code

 

特征根

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;
}
View Code

 

进制转换

转换任意进制数可以用被除数=商数×除数+余数求,余数要求非负,所以在转化-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]]);
}
View Code

 

posted @ 2015-08-29 11:30  Rivendell  阅读(607)  评论(0编辑  收藏  举报