题目:Fansblog
题意:给你一个素数n,然后让你找到小于n的最大素数Q,问Q!mod p == ?.
思路:
(
p
−
2
)
!
 
m
o
d
 
p
≡
P
(
 
m
o
d
 
p
)
(p-2)! \bmod p \equiv P(\bmod p)
(p−2)!modp≡P(modp)
∵
Q
<
n
\because Q<n
∵Q<n
∴
Q
1
!
⋯
(
p
−
3
)
(
p
−
2
)
 
m
o
d
 
p
≡
1
(
 
m
o
d
 
p
)
\therefore Q_{1} ! \cdots(p-3)(p-2) \bmod p \equiv 1(\bmod p)
∴Q1!⋯(p−3)(p−2)modp≡1(modp)
(
Q
+
1
)
⋯
(
P
−
3
)
(
p
−
2
)
=
X
(Q+1) \cdots(P-3)(p-2)=X
(Q+1)⋯(P−3)(p−2)=X
Q
!
⋅
x
 
m
o
d
 
p
≡
1
mod
p
Q ! \cdot x \bmod p \equiv 1 \operatorname{mod} p
Q!⋅xmodp≡1modp
Q
!
 
m
o
d
 
p
≡
1
⋅
x
−
1
(
 
m
o
d
 
p
)
Q ! \bmod p \equiv 1 \cdot x^{-1}(\bmod p)
Q!modp≡1⋅x−1(modp)
所以采用Miller_Rabin算法去查找最近的一个素数然后求逆元。
#include<stdio.h>
#include<string>
#include<algorithm>
#include <stdlib.h>
#include<iostream>
#include<time.h>
using namespace std;
#define LL long long
const int S = 8;
LL factor[100];
int tol;
LL mult_mod(LL a, LL b, LL c)
{
a %= c;
b %= c;
LL ret = 0;
LL tmp = a;
while(b)
{
if(b&1)
{
ret += tmp;
if(ret > c)ret -= c;
}
tmp <<= 1;
if(tmp > c) tmp -= c;
b >>= 1;
}
return ret;
}
LL pow_mod(LL a, LL n, LL mod)
{
LL ret = 1;
LL temp = a % mod;
while(n)
{
if(n & 1)ret = mult_mod(ret, temp, mod);
temp = mult_mod(temp, temp, mod);
n >>= 1;
}
return ret;
}
bool check(LL a, LL n, LL x, LL t)
{
LL ret = pow_mod(a, x, n);
LL last = ret;
for(int i = 1; i <= t; i++)
{
ret = mult_mod(ret, ret, n);
if(ret == 1 && last != 1 && last != n-1)return true;
last = ret;
}
if(ret != 1)return true;
else return false;
}
bool MiLLer_Rabin(LL n)
{
if(n < 2)return false;
if(n == 2)return true;
if((n & 1) == 0)return false;
LL x = n-1;
LL t = 0;
while((x & 1) == 0)
{
x >>= 1;
t++;
}
srand(time(NULL));
for(int i = 0; i < S; i++ )
{
LL a = rand()%(n-1)+1;
if(check(a, n, x, t))
return false;
}
return true;
}
int main()
{
int T;
cin>>T;
while(T--)
{
LL n;
scanf("%lld", &n);
LL t = n-2;
while(!MiLLer_Rabin(t))
{
t --;
}
LL ans = 1;
for(LL i = t+1; i <= n-2; i++ )
{
ans = mult_mod(ans, i, n);
}
ans = pow_mod(ans, n-2, n);
printf("%lld\n", ans);
}
return 0;
}
题目:Find the answer
题意:本题就是给你一个数组,让你判断再i之前最少需要把几个数值为0使得[1, i]之间的和小于m。
思路:本题最朴素的思想就是选择[i~i-1]之间从最大开始减直到满足为止,但是肯定会超时的所就需要带权线段树维护,但是在维护的过程中数据范围比较大,所以就需要离散化。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn = 2e5+10;
LL sum[maxn<<2];
LL C[maxn<<2];
LL t[maxn], K[maxn];
LL n, m, T;
void update(LL o, LL l, LL r, LL val){
if(l == r){
sum[o]++;
C[o] += K[val];
return;
}
LL mid = l+((r-l)>>1);
if(val <= mid)update(o<<1, l, mid, val);
if(val > mid)update(o<<1|1, mid+1, r, val);
sum[o] = sum[o<<1|1]+sum[o<<1];
C[o] = C[o<<1]+C[o<<1|1];
}
LL query(LL o, LL l, LL r, LL k){
if(l == r)return k >= sum[o] ? C[o]:C[o]/sum[o]*k;
LL ans = 0, mid = l+((r-l)>>1);
if(sum[o<<1|1] >= k){
ans += query(o<<1|1, mid+1, r, k);
}
else {
ans += C[o<<1|1];
ans += query(o<<1, l, mid, k-sum[o<<1|1]);
}
return ans;
}
LL en;
int bsearch_1(int l, int r, LL sum, LL val)
{
while (l < r)
{
int mid = l + r >> 1;
if (m-(sum-query(1, 1, en, mid)) >= val) r = mid;
else l = mid + 1;
}
return l;
}
LL getId(LL x){
return lower_bound(K+1, K+1+en, x)-K;
}
int main(){
scanf("%d", &T);
while(T--){
memset(sum, 0, sizeof sum);
memset(C, 0, sizeof C);
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++){
scanf("%lld", &t[i]);
K[i] = t[i];
}
sort(K+1, K+1+n);
en = unique(K+1,K+1+n)-K-1;
LL sum = 0;
for(int i = 1; i <= n; i++){
if(i == 1){
printf("0 ");
update(1, 1, en, getId(t[i]));
sum += t[i];
continue;
}
LL l = 0, r = i-1;
LL ans = r;
LL z = bsearch_1(l, r, sum, t[i]);
ans = min(ans, z);
printf("%lld ", ans);
update(1, 1, en, getId(t[i]));
sum += t[i];
}
printf("\n");
}
return 0;
}