思路:对于a<=x<=b,c<=y<=d.
满足条件的结果为ans=f(b,d)-f(b,c-1)-f(a-1,d)+f(a-1,c-1)。
而函数f(a,b)是计算0<=x<=a,0<=y<=b满足条件的结果。这样计算就很方便了。
LL gcd(LL x , LL y){
return y==0 ? x : gcd(y , x%y) ;
}
LL p , m ;
LL ans(LLa , LL b){
if(a < 0 || b < 0) return0 ;
LL s = 0 ;
LL ma = a % p , mb = b % p ;
s += (a/p)*(b/p) * p ;
s += (ma+1)*(b/p) + (mb+1)*(a/p) ;
if(ma > m){
s += min(m , mb) + 1 ;
LL t = (p+m-ma) % p ;
if(t <= mb)s += mb-t+1 ;
}
else{
LL t = (p+m-ma) % p ;
if(t <= mb) s += min(mb-t+1 , m-t+1) ;
}
return s ;
}
int main(){
int t , T = 1 ;
LL a , b , c , d , s , g , f ;
cin>>t ;
while(t--){
cin>>a>>b>>c>>d>>p>>m ;
s = ans(b,d) - ans(c-1 , b) - ans(a-1,d) + ans(a-1 , c-1) ;
f = (d-c+1) * (b-a+1) ;
g = gcd(f , s) ;
printf("Case #%d: %I64d/%I64d\n" , T++ , s/g , f/g) ;
}
return 0 ;
}
A(0,0),B(p,0),C(m,q),D(m,n);
因为(f,g)不相交数=(f,g)总数-(f,g)相交数;
总数: c(m+n,m)*c(m-p+q,q);
相交数:c(m+q,m)*c(m-p+n,n);
const LL mod = 100000007 ;
LL exgcd(LL a ,LL b ,LL &x ,LL &y){
if(b==0){
x=1 ;
y=0 ;
return a ;
}
LL g=exgcd(b,a%b,x,y) ;
LL t=x ;
x=y ;
y=t-(a/b)*y ;
return g ;
}
LL inverse(LL n){
LL x , y ;
exgcd(n , mod , x , y) ;
return (x % mod + mod) % mod ;
}
LL C(LL n , LL m){
LL u = 1 , v = 1 ;
for(LL i = 0 ; i < m ; i++){
u = u * (n-i) % mod ;
v = v * (i+1) % mod ;
}
return u * inverse(v) % mod ;
}
int main(){
LL s , m , n , p , q ;
while(cin>>m>>n>>p>>q){
s = C(m+n , n) * C(m+q-p , q) % mod ;
s -= C(m+q,m) * C(m+n-p,n) % mod ;
s = (s % mod + mod) % mod ;
cout<<s<<endl ;
}
return 0 ;
}
令t=x1+x2,则t一定是m的约数,所以应枚举m的所有约数。
然后可以得到
t = x1+x2
n/t = x1^2 - x1*x2 +x2^2 ;
= t*t – 3x1*x2
ð x1*x2 =(t*t-n/t) / 3 = m ;
有韦达定理 x1,x2为二次方程 x^2 – mx + t = 0的2正整数根
再就是注意范围要用unsigned long long。
typedef unsigned long long ll ;
const ll M = 3000001 ;
int cnt,num,cn;
ll prime[M],p[100],e[100];
bool f[M];
ll n;
void make(){
cnt=0;
for(int i=2;i<M;i++){
if(!f[i]) prime[cnt++]=i;
for(int j=0;j<cnt&&i*prime[j]<M;j++){
f[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
}
void fac(ll m){
num=0;
for(ll i=0;i<cnt&&(ll)prime[i]*prime[i]<=m;i++){
if(m%prime[i]==0){
p[num]=prime[i];
ll j=1;
m/=prime[i];
while(m%prime[i]==0){
j++;
m/=prime[i];
}
e[num++]=j;
}
}
if(m>1){
p[num]=m;
e[num++]=1;
}
}
ll ispw2(ll a){
ll b=sqrt(1.0*a);
if((ll)b*b==a) return b;
return M;
}
map<ll , ll> answer ;
map<ll , ll> ::iterator it ;
void is(ll t){
ll x1,x2;
ll p=n/t;
ll a=t*t-p;
if(a>0&&a%3!=0) return ;
ll m=a/3;
ll b=t*t-4*m;
if(b<0) return ;
ll c=ispw2(b);
if(c==M) return ;
if((t+c)%2==0){
x1=(t+c)/2;
x2=t-x1;
if(x1>x2) swap(x1,x2);
if(x1>0&&x1<t&&x2>0&&x2<t&&answer.find(x1) == answer.end()){
answer[x1] = x2 ;
}
}
}
ll Pow(ll x,ll y){
ll s=1;
for(;y;y>>=1 , x*=x){
if(y&1) s*=x;
}
return s;
}
void dfs(int id,ll s){
is(s);
if(id>=num) return;
for(int i=0;i<=e[id];i++)
dfs(id+1, (ll)s*Pow(p[id],i));
}
int main(){
make();
while(scanf("%llu",&n)!=EOF){
fac(n);
cn=0;
answer.clear() ;
dfs(0,1);
printf("%d",answer.size()) ;
for(it = answer.begin() ;it != answer.end() ; it++)
printf(" (%llu,%llu)",it->first,it->second);
printf("\n");
}
return 0;
}
容斥原理,不考虑约束条件,则一共有 N! 种复习方案,然后减去刚好触发一个约束条件的方案数,加上刚好触发两个约束条件的方案数,减去刚好触发三个的……依此类推
另外注意一点,输入中可能包含重复的约束条件,要先去重再算容斥,否则会得到错误的结果
题意抽象出来就是 在 n*n 的 棋盘中 加了 m 个禁位, 放置 n 个棋子 ,每两个棋子不在同一行 同一列,问有多少中放置方式 ?
trick :
M 个禁位 中 有相同 坐标的点。
解法 : 利用 有禁位的排列的公式 (容斥原理):
n! - r1 *( n-1)! + r2*(n -2)! - r3*(n-3)! +..........
ri 指 在禁区中 选 i 个 位置的 方案数。
由于 m 的值较小 ,可直接 dfs暴力求方案数。
const LL mod = 55566677 ;
int x[28] , y[28] ;
LL p[60] ;
bool visx[60] , visy[60] ;
int n , m ;
LL s ;
void dfs(int cnt , int id , int f){
s = (s + p[n-cnt]*f) % mod ;
s = (s+mod) % mod ;
for(int i = id+1 ; i < m ; i++){
if(!visx[x[i]] && !visy[y[i]]){
visx[x[i]] = visy[y[i]] = 1 ;
dfs(cnt+1 , i , -f) ;
visx[x[i]] = visy[y[i]] = 0 ;
}
}
}
int main(){
int i , j ;
p[0] = p[1] = 1 ;
for(i = 2 ; i < 60 ; i++) p[i] = p[i-1] * i % mod ;
while(cin>>n>>m){
for(i = 0 ; i < m ; i++) scanf("%d%d" ,&x[i] , &y[i]) ;
for(i = 0 ; i < m ; i++){
for(j = i+1 ; j < m ; j++){
if(x[i]==x[j] && y[i]==y[j]){
swap(x[j] , x[m-1]) ;
swap(y[j] , y[--m]) ;
}
}
}
memset(visx , 0 , sizeof(visx)) ;
memset(visy , 0 , sizeof(visy)) ;
s = 0 ;
for(i = 0 ; i < m ; i++){
visx[x[i]] = visy[y[i]] = 1 ;
dfs(1 , i , 1) ;
visx[x[i]] = visy[y[i]] = 0 ;
}
s = (p[n] - s) % mod ;
s = (s + mod) % mod;
cout<<s<<endl ;
}
return 0 ;
}
限位排列
一门课的书有 N 章内容要复习,每天可以复习一章,但是第 i 章不能在第 i 天或第 i + 1 天复习(最后一章则对应最后一天和第一天),问不同的复习方案数, 递推公式如下:
int main(){
f[0]=f[3]=1, f[1]=f[2]=0, f[4]=2;
int n ;
for(n = 5 ; n <= maxn ; n++){
if(n%2)
f[n] = (f[n-1]*n%mod + (f[n-2]*n%mod + 4) * inverse(n-2)) % mod ;
else
f[n] = (f[n-1]*n%mod + (f[n-2]*n%mod - 4) * inverse(n-2)) % mod ;
}
while(scanf("%d" ,&n) != EOF){
printf("%lld\n" , f[n]) ;
}
return 0;
}
题意:求第k个斐波那契素数对应的斐波那契数列及其之后的值%x==0的最小的斐波那契数值,(%m), zoj
数据范围:(1<=k<=10^6),(3<=X<=100),(10<=M<=10^6)
解题思路:
斐波那契数列的特性:( 0,1,1,2,3,5,8... )
1、gcd(fib(n),fib(m))=fib(gcd(n,m))
2、如果fib(k)能被x整除,则fib(k*i)都可以被x整除。
3、f(0)+f(1)+f(2)+…+f(n)=f(n+2)-1
4、f(1)+f(3)+f(5)+…+f(2n-1)=f(2n)
5、f(2)+f(4)+f(6)+…+f(2n) =f(2n+1)-1
6、[f(0)]^2+[f(1)]^2+…+[f(n)]^2=f(n)·f(n+1)
7、f(0)-f(1)+f(2)-…+(-1)^n·f(n)=(-1)^n·[f(n+1)-f(n)]+1
8、f(m+n)=f(m-1)·f(n-1)+f(m)·f(n)
9、[f(n)]^2=(-1)^(n-1)+f(n-1)·f(n+1)
10、f(2n-1)=[f(n)]^2-[f(n-2)]^2
11、3f(n)=f(n+2)+f(n-2)
12、f(2n-2m-2)[f(2n)+f(2n+2)]=f(2m+2)+f(4n-2m)[ n〉m≥-1,且n≥1]
还有一个结论:
计算(a/b)%c 其中b能整除a
如果b与c互素,则(a/b)%c=a*b^(phi(c)-1)%c
如果b与c不互素,则(a/b)%c=(a%bc)/b
对于b与c互素和不互素都有(a/b)%c=(a%bc)/b成立
斐波那契素数的定义:( 2,3,5,13,89... )
对于斐波那契数列的第i个元素s[i],如果gcd(s[i],s[j])==1(1<=j<i),那么s[i]是一个斐波那契素数。
斐波那契素数的特性(可根据斐波那契数列求出斐波那契素数):
对于斐波那契数列(0,1,1,2,3,5,8...),下标从0开始,下标为3,4,5,7,11,13...的数就是斐波那契素数。
即除了前两个外,斐波那契数列的素数下标对应的就是斐波那契素数,而如果下标不是素数,那么对应的也不是斐波那契素数。
所以这一题就先筛出斐波那契素数下标(3,4,5,7,11,13...),然后就可以知道第k个斐波那契素数对应的斐波那契数列的下标,
根据矩阵快速幂计算出斐波那契数值,判断是否%x==0,如果不是就继续找下一个,直到找到为止。最后a/x%m=(a%(x*m))/x
typedef long long LL;
const int maxn = 15500000 ;
int primesize ;
int prime[maxn + 8] ;
bool is[maxn + 8] ;
void make(){
primesize = 0 ;
memset(is , 0 , sizeof(is)) ;
for(int i = 2 ; i <= maxn ; i++){
if(! is[i]) prime[++primesize] = i ;
for(int j = 1 ; j <= primesize && prime[j]*i <= maxn ; j++){
is[prime[j]*i] = 1 ;
if(i % prime[j] == 0) break ;
}
}
prime[1] = 3 ;
prime[2] = 4 ;
}
LL mod ;
struct Mat{
LL x[3][3] ;
Mat(){
x[1][1] = 0 , x[1][2] = 0 ;
x[2][1] = 0 , x[2][2] = 0 ;
}
Mat(int){
x[1][1] = 1 , x[1][2] = 0 ;
x[2][1] = 0 , x[2][2] = 1 ;
}
Mat(LL a11 , LL a12 , LL a21 , LL a22){
x[1][1] = a11 , x[1][2] = a12 ;
x[2][1] = a21 , x[2][2] = a22 ;
}
};
Mat operator * (Mat A , Mat B){
Mat s ;
for(int i = 1 ; i <= 2 ; i++){
for(int j = 1 ; j <= 2 ; j++){
for(int k = 1 ; k <= 2 ; k++){
s.x[i][j] += A.x[i][k] * B.x[k][j] ;
s.x[i][j] %= mod ;
}
}
}
return s ;
}
Mat operator ^ (Mat x , int y){
Mat s(1) ;
for(; y ; y>>=1){
if(y&1) s = s * x ;
x = x * x ;
}
return s ;
}
const Mat F(1 , 1 , 1, 0) ;
int main(){
make() ;
LL k , x , m ;
int i , t ;
Mat A ;
cin>>t ;
while(t--){
scanf("%lld%lld%lld" ,&k , &x , &m) ;
int id = prime[k] ;
mod = x ;
for(i = id ; ; i++){
A = F ^ (i-1) ;
if(A.x[1][1] % x == 0) break ;
}
mod = x * m ;
A = F ^ (i-1) ;
printf("%lld\n" , A.x[1][1]/x) ;
}
return 0 ;
}
定理:
设gcd(a,m)=1,必有正整数x,使得a^x=1(mod m),且设满足等式的最小正整数为x0,必满足x0|phi(m).注意m>1.
否则如果gcd(a,m)!=1,则方程a^x=1(modm)没有解。
const int maxn = 70000 ; int prime[maxn] ; int primesize ; bool is[maxn+10] ; void make(){ memset(is , 0 , sizeof(is)) ; primesize = 0 ; for(int i = 2 ; i <= maxn ; i++){ if(! is[i]) prime[primesize++] = i ; for(int j = 0 ; j < primesize && i*prime[j] <= maxn ; j++){ is[i*prime[j]] = 1 ; if(i % prime[j] == 0) break ; } } } int phi(int n){ int m = sqrt(0.5 + n) ; int s = n ; for(int i = 0 ; i < primesize && n!=1 && prime[i] <= m ; i++){ if(n % prime[i] == 0){ s = s - s/prime[i] ; while(n % prime[i] == 0) n/=prime[i] ; } } if(n != 1) s = s - s/n ; return s ; } int gcd(int x , int y){ return y == 0 ? x : gcd(y , x % y) ; } struct Fac{ int x ; int s ; Fac(){} Fac(int _x , int _s):x(_x),s(_s){} }h[208] ; vector<int> factor ; vector<int> ::iterator it ; int facsize ; void getfac(int n){ int m = sqrt(0.5 + n) , t ; facsize = 0 ; for(int i = 0 ; i < primesize && n != 1 && prime[i] <= m ; i++){ if(n % prime[i] == 0){ t = 0 ; while(n % prime[i] == 0){t++ ; n/=prime[i] ;} h[facsize++] = Fac(prime[i] , t) ; } } if(n != 1) h[facsize++] = Fac(n , 1) ; } int Pow(int x , int y){ int s = 1 ; for(;y;y>>=1 , x *= x){ if(y&1) s *= x ; } return s ; } int Pow(int x , int y , int m){ int s = 1 ; for(;y;y>>=1 , x *= x , x %= m){ if(y&1) s *= x , s %= m ; } return s ; } void dfs(int id , int n){ if(id == facsize){ factor.push_back(n) ; return ; } for(int i = 0 ; i <= h[id].s ; i++) dfs(id+1 , n*Pow(h[id].x , i)) ; } void getfactor(int n){ factor.clear() ; getfac(n) ; dfs(0 , 1) ; sort(factor.begin() , factor.end()) ; } int answer(int a , int m){ getfactor(phi(m)) ; for(it = factor.begin() ; it != factor.end() ; it++){ if(Pow(a , *it , m) == 1) return *it ; } } int main(){ make() ; int n , i , ph ; while(scanf("%d" ,&n) != EOF){ if(n == 1 || gcd(2 , n) != 1){ printf("2^? mod %d = 1\n" , n) ; continue ; } printf("2^%d mod %d = 1\n" , answer(2 , n) , n ) ; } return 0 ; }
写法2
LL phi(LL n){ LL s = n ; LL m = (LL)sqrt(0.5 + n) ; for(LL i = 2 ; n!=1 && i <= m ; i++){ if(n % i == 0){ s = s - s/i ; while(n % i == 0) n /= i ; } } if(n != 1) s = s - s/n ; return s ; } LL gcd(LL x , LL y){ return y == 0 ? x : gcd(y , x % y) ; } vector<LL> factor ; vector<LL> ::iterator it ; void getfactor(LL n){ factor.clear() ; LL m = (LL)sqrt(0.5 + n) ; for(LL i = 1 ; i <= m ; i++){ if(n % i == 0){ factor.push_back(i) ; if(i * i != n) factor.push_back(n/i) ; } } sort(factor.begin() , factor.end()) ; } LL Pow(LL x , LL y , LL m){ LL s = 1 ; for(;y;y>>=1 , x *= x , x %= m){ if(y&1) s *= x , s %= m ; } return s ; } LL answer(LL a , LL m){ getfactor(phi(m)) ; for(it = factor.begin() ; it != factor.end() ; it++){ if(Pow(a , *it , m) == 1) return *it ; } } int main(){ LL n , i ; while(scanf("%I64d" ,&n) != EOF){ if(n == 1 || gcd(2 , n) != 1){ printf("2^? mod %lld = 1\n" , n) ; continue ; } printf("2^%I64d mod %I64d = 1\n" , answer(2 , n) , n ) ; } return 0 ; }
题目大意是:在一个平面上有N个点,每个点的坐标已经给出,现在要求在X轴上找一个点,使得这个点到所有点中最大的距离最小。
分析:我们设这个点为X0,所求的距离为F(x),那么对于所有的 X < X0 和 X > X0 都有F(x) > ans,即实际上这个函数是已X0为最小值,两边
都是单调递增或者递减的,因此我们可以三分查找找出这个最值的坐标。总复杂度O(n*logn).
const double EPS=1e-9; const int maxn = 50008 ; int n ; double x[maxn] , y[maxn] ; double f(double nx){ double s = 0 , d ; for(int i = 1 ; i <= n ; i++){ d = sqrt(y[i]*y[i] + (x[i]-nx)*(x[i]-nx)) ; if(s < d) s = d ; } return s ; } //三分 对凹(凸)函数判断最小(大)值,此题是求最小值。 //不要求左l右r值的大小比较,即(L<R 或 L>=R)都可 double tri_search(double l , double r){ double Mid , Midmid , L ,R ; L = l ; R = r ; while( L + EPS < R ){ // 由于L ,R没有要求谁大谁小,故求的绝对值 Mid=(L+R)*0.5; Midmid=(Mid+R)*0.5; if(f(Mid)<= f(Midmid) ) R=Midmid; else L=Mid; } return (L+R)*0.5; } int main(){ int i ; double l , r , nx ; while(cin>>n && n){ l = 200008 ; r = - 200008 ; for(i = 1 ; i <= n ; i++){ scanf("%lf%lf",&x[i] ,&y[i]) ; l = min(l , x[i]) ; r = max(r , x[i]) ; } nx = tri_search(l,r) ; printf("%.9lf %.9lf\n" , nx , f(nx)) ; } return 0 ; }