HGOI 20190519 题解
脑补了一下今天的比赛难度和之前zju-lzw出的题目画风迥异。
难度完全不是一个水平的好伐。
Probem A palindrome
给出一个$n$个元素的数组,可以任意指定一个数字$m$让所有$a_i = a_i \% m$。
使得最终得出的数组成为形如$\{1,2,3,2,1\}$的回文数组,求最大的$m$。
对于100%的数据$1\leq n \leq 10^5,1 \leq a_i \leq 10^9$
Sol: 我们要求同余方程 $ \left\{\begin{matrix} a_1 \equiv a_n (mod \ m)\\ ...\\ a_i \equiv a_{n-i+1}(mod \ m)\\ ...\\ a_n \equiv a_1(mod \ m)\\ \end{matrix}\right.$的最大解$m$,
显然的我们考虑一种普遍的情况$a \equiv b (mod \ p)$。
定理:上述结论成立的充分必要条件是$|a-b| \equiv 0 (mod \ p)$
充分性:设$a \leq b$ 那么有 $b-a = kp$即$b = kp + a$显然$a \equiv b (mod \ p) $
必要性:证明同理。 所以这两个命题是等价的关系。
所以我们只要做差求gcd 就可以了。
# include <bits/stdc++.h> using namespace std; const int N=1e5+10; int a[N]; int gcd(int a,int b) { if (b==0) return a; return gcd(b,a%b); } int main() { int n; scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); int ans=0; for (int i=1;i<=n/2;i++) ans=gcd(ans,abs(a[n-i+1]-a[i])); if (ans==0) puts("-1"); else printf("%d\n",ans); return 0; }
Probem B factorial
给出一个序列$\{a_i\}$,完成三种操作:
1.区间 [l,r] 所有数+1
2.询问区间 [l,r] $\sum\limits_{i=l}^{r} a_i ! \ mod 10^9$
3.单点更新pos,val
对于100%的数据$n,m \leq 10^5 \ val,a_i\leq 10^9$
Sol: 都9102年了还是有人不知道线段树的骚操作。
模数是个合数你有什么好说的,一定有鬼。
发现$40! \ mod 10^9 = 0$于是我们所有的操作都打上了 $40$次的限制。
不需要懒标记,直接下放到叶子节点就可以。
复杂度$O(40\times n log_2 n)$
# include <bits/stdc++.h> # define int long long using namespace std; const int N=1e5+10; const int mo=1e9; int t[N<<2],s[50],g[N<<2],a[N]; int n,m; void build(int x,int l,int r) { if (l==r) { g[x]=a[l]; g[x]=min(g[x],41ll); t[x]=s[g[x]]; return;} int mid=(l+r)/2; build(2*x,l,mid); build(2*x+1,mid+1,r); t[x]=(t[2*x]+t[2*x+1])%mo; g[x]=g[2*x]+g[2*x+1]; } void update1(int x,int l,int r,int ql,int qr) { if (g[x]>=(r-l+1)*41) return; if (l==r) { g[x]++; g[x]=min(g[x],41ll); t[x]=s[g[x]]; return;} int mid=(l+r)/2; if (ql<=mid) update1(2*x,l,mid,ql,qr); if (qr>mid) update1(2*x+1,mid+1,r,ql,qr); t[x]=(t[2*x]+t[2*x+1])%mo; g[x]=g[2*x]+g[2*x+1]; } void update2(int x,int l,int r,int pos,int val) { if (l==r) { g[x]=val; g[x]=min(g[x],41ll); t[x]=s[g[x]]; return;} int mid=(l+r)/2; if (pos<=mid) update2(2*x,l,mid,pos,val); else update2(2*x+1,mid+1,r,pos,val); t[x]=(t[2*x]+t[2*x+1])%mo; g[x]=g[2*x]+g[2*x+1]; } int query(int x,int l,int r,int ql,int qr) { if (ql<=l&&r<=qr) return t[x]; int mid=(l+r)/2,ret=0; if (ql<=mid) ret=(ret+query(2*x,l,mid,ql,qr))%mo; if (qr>mid) ret=(ret+query(2*x+1,mid+1,r,ql,qr))%mo; return ret; } signed main() { scanf("%lld%lld",&n,&m); for (int i=1;i<=n;i++) { scanf("%lld",&a[i]); } s[0]=1; for (int i=1;i<=45;i++) s[i]=s[i-1]*i%mo; s[0]=0; build(1,1,n); while (m--) { int op,l,r; scanf("%lld%lld%lld",&op,&l,&r); if (op==1) { update1(1,1,n,l,r); } else if (op==2) { printf("%lld\n",query(1,1,n,l,r)); } else if (op==3) { update2(1,1,n,l,r); } } return 0; }
Problem C triangle
定义函数$f(x) = $为一条直角边为$x$的正整数边长直角三角形种类数。
给出$q$组询问,每个询问含有一个值$n$ ,询问$f(x) = n$的最小整数解$x_{min}$
对于100%的数据,$q\leq 10^5 , 1\leq n \leq 10^6$
Sol : $n^2 +b^2 = c^2 $得出$n^2=(c+b)(c-b)-st(s=c+b,t=c-b)$
解得$c = \frac{s+t}{2} , b = \frac{s-t}{2} , t < s$。
结合三角形是边长是整数,即$b,c \in Z$
所以任意解$(s,t)$可以用$st = n^2,s > t, s \equiv t (mod \ 2)$
所以我们估计$f(n) = \frac{d(n^2)}{2}$规模的 , 手玩一下我们会发现。
对$n$质因数分解$n = 2^{k_2} \times 3 ^{k_3} \times 5^{k_5} ... $
所以$n^2 = 2^{2k_2} \times 3 ^{2k_3} \times 5^{2k_5} ...$
由于$s,t$的奇偶性必须同奇同偶,所以$2k_2$个$2$不能全分给$s,t$减少了一些方案。
其质因子需要任意分。三角形边长不能为0,所以排除$s=t$的情况,还需-1。
所以$f(n) = \frac{2{k_2}(2{k_3}+1)(2{k_5}+1)...}{2}$
令$f(x) = n$,对于选中的$x$,需要最小,使得$2{k_2}(2{k_3}+1)(2{k_5}+1)... = 2n+1$
用若干(取个14个就够了)个小素数来组成$m$可以保证m最小,依次考虑每次质数,背包一下就行了。
复杂度$O(qk^2) , k =14$
#include <bits/stdc++.h> using namespace std; #define N 2000010 typedef long long LL; LL F[N]; LL temp[N]; const LL lim = 1e16; int p[20] = {14, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 33, 37, 41}; void up(LL &x, LL y) { if (x == 0) x = y; else x = min(x, y);} int main() { F[1] = 1; for (int i = 1; i <= p[0]; ++i) { memset(temp, 0, sizeof(temp)); for (int j = 1; j < N; ++j) { if (F[j] == 0) continue; int kk = 0; LL s = 1; while (true) { if (j*(kk*2+1) >= N) break; if (F[j] > lim / s) break; if (i > 1 || kk == 0) up(temp[j*(kk*2+1)], F[j]*s); ++kk; if (s > lim / p[i]) break; s = s * p[i]; } } memcpy(F, temp, sizeof(temp)); } int T, n; scanf("%d", &T); while (T--) { scanf("%d", &n); LL ans = lim + 1; if (F[2*n+1]) ans = min(ans, F[2*n+1]); LL w = 2; for (int k = 1; k < 60; ++k, w = w * 2) { if ((2*n+1) % (2*k-1)) continue; LL t = F[(2*n+1)/(2*k-1)]; if (t && t <= lim / w) ans = min(ans, t*w); } if (ans == lim+1) ans = -1; printf("%lld\n", ans); } return 0; }