Nothing for Nothing(十四)

题目: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) (p2)modpP(modp)
∵ Q &lt; n \because Q&lt;n Q<n
∴ Q 1 ! ⋯ ( p − 3 ) ( p − 2 ) &VeryThinSpace; m o d &VeryThinSpace; p ≡ 1 ( &VeryThinSpace; m o d &VeryThinSpace; p ) \therefore Q_{1} ! \cdots(p-3)(p-2) \bmod p \equiv 1(\bmod p) Q1!(p3)(p2)modp1(modp)
( Q + 1 ) ⋯ ( P − 3 ) ( p − 2 ) = X (Q+1) \cdots(P-3)(p-2)=X (Q+1)(P3)(p2)=X
Q ! ⋅ x &VeryThinSpace; m o d &VeryThinSpace; p ≡ 1 mod ⁡ p Q ! \cdot x \bmod p \equiv 1 \operatorname{mod} p Q!xmodp1modp
Q ! &VeryThinSpace; m o d &VeryThinSpace; p ≡ 1 ⋅ x − 1 ( &VeryThinSpace; m o d &VeryThinSpace; p ) Q ! \bmod p \equiv 1 \cdot x^{-1}(\bmod p) Q!modp1x1(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;
}

09-14 4245
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值