2017福建夏令营Day7(数论)

埃匹希斯水晶 (apexis)

⼤家都知道,卡德加是⼀个神奇的法师。 有⼀天,他发现了⼀种可以作⽤在埃匹希斯⽔晶上的魔法:在左右两个 祭坛上放⼀定量的⽔晶,然后施放⼀个法术,左边⼀堆的⽔晶数量会变成原 来两个祭坛上⽔晶之和,右边⼀堆会变成两个祭坛上⽔晶数量之差。卡德加现在有两堆⽔晶,分别有 A 个和 B 个。他打算集中精⼒连续 释放 N 次法术,但不知道最后能拿到多少⽔晶,于是他找到了要塞指挥官 (就是你了)。

输入 三个整数 A, B, N,表⽰祭坛上刚开始的⽔晶数,和法术的释放次数。

输出 两个数,祭坛上最后的⽔晶数。输出模 (109 + 7)。

样例输入 123

样例输出 6 2

数据规模 50%: N ≤ 106。 100%: A, B ≤ 109, N ≤ 1018

题解
每次操作后a,b都乘2
快速幂即可

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
const int M=1e9+7;
long long a,b,n,nn,m;
inline ll qpow(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1)      ans=(ans*a)%M;
        b>>=1;
        a=(a*a)%M;
    }
    return ans%M;
}
int main()
{
    freopen("apexis.in","r",stdin);
    freopen("apexis.out","w",stdout);
    cin>>a>>b>>n;
    if(n==0)
    {
        cout<<a%M<<" "<<b%M<<endl;
        return 0;
    }
    if(a>=b)
    {
        if(n&1)
        {
            nn=a+b;
            m=a-b;
        }
        else
        {
            nn=a;
            m=b;
        }
    }
    else
    {
        if(n&1)
        {
            nn=a+b;
            m=b-a;
        }
        else
        {
            nn=b;
            m=a;
        }
    }
    n>>=1;
    cout<<(qpow(2,n)*(nn%M))%M<<" "<<(qpow(2,n)*(m%M))%M<<endl;
    return 0;
}

标签

快速幂,细节

 

要塞任务 (quest)

你的要塞⾥有 N 名随从,每名随从有⼀个战⽃⼒值 Ai,不同随从的战 ⽃⼒可以相同。⼀个要塞任务需要恰好 M 个随从参与。 要塞任务的奖励取决于随从们配合的程度。(显⽽易见地),M 个随 从的联合战⽃⼒ A 为它们战⽃⼒的最⼤公约数,⽽任务的奖励分数定义为 φ(A)。 求最⼤可能的奖励分数。

输入 本题有多组数据,第⼀⾏为数据组数 T。 接下来每组数据有两⾏,第⼀⾏两个整数 N,M,第⼆⾏ N 个整数 Ai。

输出 最多的奖励分数。

样例输入 1 5 2 1 4 6 9 12

样例输出 2 派出编号为 6 和 9 的随从,联合战⽃⼒为 3,奖励分数 2。

数据规模 20%:NM ≤ 105。 60%:N,M,Ai ≤ 100。 100%:N,M,Ai ≤ 100000,T ≤ 10。

 

题解
先预处理线性筛欧拉函数
如何处理出当前x是否是m个数的最大公因数?
num[x]记录n个数中x的数量
对于当前的x的倍数数量为num[x]+num[2x]+num[3x]...
只要x倍数的数量大于等于m,它一定是其中m个数(取他的倍数的数)的最大公因数
然后用x的欧拉函数值更新答案即可

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
const int M=100000;
int ans=-1;
int b[M+5],prime[M+5],ou[M+5],cnt=0,num[M];
inline void init()
{
    for(int i=2;i<=M;i++)
    {
        if(!b[i])
        {
            prime[++cnt]=i;
            ou[i]=i-1;
        }
        for(int j=1;prime[j]*i<=M;j++)
        {
            b[prime[j]*i]=1;
            if(i%prime[j]==0)    break;
        }
    }
    ou[1]=1;
    int t;
    for(int i=2;i<=M;i++)
        if(b[i])
        {
            ou[i]=i;
            t=i;    
            for(int j=1;j<=cnt&&t>1;j++)
            {
                if(i%prime[j]==0)
                {
                    ou[i]=ou[i]*(prime[j]-1)/prime[j];
                    while(t%prime[j]==0)
                        t/=prime[j];
                }
                if(prime[j]*2>i)    break;
            }
        }
}
int main()
{
    freopen("quest.in","r",stdin);
    freopen("quest.out","w",stdout);
    int T,tot,n,m;
    init();
    scanf("%d",&T);
    while(T--)
    {
        ans=-1;
        int maxx=-1;
        memset(num,0,sizeof(num));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            num[x]++;
            maxx=std::max(maxx,x);
        }
        for(int i=1;i<=maxx;i++)
        {
            tot=0;
            for(int j=i;j<=maxx;j+=i)
            {
                tot+=num[j];
            }
            if(tot>=m)
                ans=std::max(ans,ou[i]);
        }
        printf("%d\n",ans);
    }
    return 0;
}

标签

最大公因数,欧拉函数

 

卡德加的兔子 (rabbit)

样例输出 2 5 4 8 9 16 121

第⼀个法术过后,前 3 个笼⼦各有 2 对兔⼦,其余笼⼦有 1 对。 第⼆个法术过后,第 4~7 个笼⼦有 3 对兔⼦,其余不变。 第三个法术过后,前 3 个笼⼦有 13 对兔⼦,第 4~7 个笼⼦有 21 对兔 ⼦,第 8~9 个笼⼦有 5 对兔⼦,最后⼀个笼⼦有 1 对兔⼦。

数据规模 N,M ≤ 100 N,M ≤ 100000 K ≤ 5 20% 20% K ≤ 109 20% 40%(*) (*):数据有梯度。 (注:刚才有⼈问这个表格的意思。所有数据⼀共 100%,前 20% 有 N,M ≤ 100 且 K ≤ 5,接下来 20% 有 N,M ≤ 100 且 K ≤ 109,以 此类推。最后数据有梯度的意思是后 40% 的数据存在⾮极限数据(⽐如 N,M = 10000 这样的)。)


题解
线段树叶子节点维护斐波那契数的转移矩阵,非叶子节点维护转移矩阵之和
整段加=整段矩阵乘上F(基本矩阵)^k
查区间和=整段矩阵左上角元素求和
需要快速幂算F^k
线段树+矩阵快速幂的题目

代码(std)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#define rg register

const int mod = 10007;

struct matrix {
    int n, m;
    int a[3][3];
    matrix() {
        n = m = 2;
        a[0][0] = a[0][1] = a[0][2] = 
        a[1][0] = a[1][1] = a[1][2] = 
        a[2][0] = a[2][1] = a[2][2] = 0;
    }
    matrix operator*(rg const matrix &b) {
        matrix res;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= b.m; j++)
                for(int k = 1; k <= m; k++)
                    res.a[i][j] += a[i][k]*b.a[k][j], res.a[i][j] %= mod;
        return res;
    }
    matrix operator+(rg const matrix &b) {
        matrix res;
        for(int i = 1; i <= n; i++)
            for(rg int j = 1; j <= m; j++) {
                res.a[i][j] = a[i][j]+b.a[i][j];
                if(res.a[i][j] >= mod) res.a[i][j] -= mod;
            }    
        return res;
    }
}begin, mul, one, nul;

void initmat() {
    begin.a[1][1] = 1;
    begin.a[1][2] = 0;
    mul.a[1][1] = mul.a[1][2] 
    = mul.a[2][1] = 1;
    one.a[1][1] = one.a[2][2] = 1;
}

matrix pow(rg matrix a, rg int k) {
    if(k == 0) return one;
    if(k == 1) return a;
    matrix b = pow(a, k>>1);
    return b*b*pow(a, k&1);
}

struct info {
    
    matrix sum, tag;
    
    int l, r;
    
} t[400010];

void push_up(rg int o) {
    t[o].sum = t[o<<1].sum+t[o<<1|1].sum;
}

void push_down(rg int o) {
    t[o<<1].sum = t[o<<1].sum*t[o].tag;
    t[o<<1|1].sum = t[o<<1|1].sum*t[o].tag;
    t[o<<1].tag = t[o<<1].tag*t[o].tag;
    t[o<<1|1].tag = t[o<<1|1].tag*t[o].tag;
    t[o].tag = one;
}

void build(rg int o, rg int l, rg int r) {
    if(l > r) return;
    t[o].l = l, t[o].r = r, t[o].tag = one;
    if(l == r) {
        t[o].sum = begin;
        return;
    }
    int mid = l+r>>1;
    build(o<<1, l, mid);
    build(o<<1|1, mid+1, r);
    push_up(o);
}

void times(rg int o, rg int l, rg int r, rg matrix x) {
    if(r < t[o].l || t[o].r < l) return;
    if(l <= t[o].l && t[o].r <= r) {
        t[o].sum = t[o].sum*x;
        //printf("%d\n", t[o].sum.a[1][1]);
        t[o].tag = t[o].tag*x;
        return;
    }
    if(t[o].l == t[o].r) return;
    push_down(o);
    times(o<<1, l, r, x);
    times(o<<1|1, l, r, x);
    push_up(o);
}

void modify(rg int l, rg int r, rg int k) {
    rg matrix x = pow(mul, k);
    times(1, l, r, x);
}

matrix sum(rg int o, rg int l, rg int r) {
    if(r < t[o].l || t[o].r < l) return nul;
    if(l <= t[o].l && t[o].r <= r) return t[o].sum;
    if(t[o].l == t[o].r) return nul;
    push_down(o); return sum(o<<1, l, r)+sum(o<<1|1, l, r);
}

int query(rg int l, rg int r) {
    return sum(1, l, r).a[1][1];
}

int main() {
    freopen("rabbit.in", "r", stdin);
    freopen("rabbit.out", "w", stdout);
    rg int n, m;
    initmat();
    scanf("%d%d", &n, &m);
    build(1, 1, n);
    while(m--) {
        rg int l, r, k;
        scanf("%d%d%d", &l, &r, &k);
        if(!k) printf("%d\n", query(l, r));
        else modify(l, r, k);
    }
    return 0;
} 

 

标签

线段树,矩阵快速幂

posted @ 2017-08-23 15:55  小白的小白  阅读(318)  评论(0编辑  收藏  举报