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; }
标签
线段树,矩阵快速幂