[UOJ 48][UR #3]核聚变反应堆
先去考虑两个变量的sgcd怎么求……我们先求出他们的gcd,然后在除掉一个最小的质因子即可。
那么我们先把a1分解质因数,然后所有数和它求一个gcd,然后去去掉一个尽可能小的质因子。注意到一个数p的质因子数量是O(logp)级别的,所以每一个数找到要除的那个最小的质因子只需要O(loga1)的时间。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | #include <cstdio> #include <cstring> #include <cstdlib> #include <cctype> #include <algorithm> #include <utility> #include <vector> #include <cmath> const int maxn = 100005; using ll = long long ; ll V[maxn]; int vcnt = 0; void des(ll x) { ll m = (ll) sqrt (x + 0.5); for (ll i = 2; i <= m; i ++) { if (x % i == 0) { V[vcnt ++] = i; while (x % i == 0) x /= i; } } if (x > 1LL) V[vcnt ++] = x; } ll gcd(ll a, ll b) { if (!b) return a; else return gcd(b, a % b); } ll A[maxn]; int main() { int n; scanf ( "%d" , &n); for ( int i = 1; i <= n; i ++) { scanf ( "%lld" , &A[i]); } des(A[1]); for ( int i = 1; i <= n; i ++) { if (i > 1) putchar ( ' ' ); ll bs = gcd(A[i], A[1]); if (bs == 1) { printf ( "-1" ); continue ; } for ( int j = 0; j < vcnt; j ++) { if (A[i] % V[j] == 0) { bs /= V[j]; break ; } } printf ( "%lld" , bs); } puts ( "" ); return 0; } |
[BZOJ 4816][SDOI2017]数字表格
当年在考场上一点反演都不会啊啊啊啊啊啊
这题虽然牵扯到了对积的反演,但其实也不是很难。先让我们大力推导一波(逃
考虑枚举柿子中的f[(i,j)]中的最大公约数(设为k),然后式子变成了(这里不妨偷个懒,钦定n≤m):
n∏k=1f[k]∑ni=1∑mj=1[(i,j)=k]
然后发现上面那个指数就是Problem b的式子,显然可化为∑nd=1μ(d)⌊nkd⌋⌊mkd⌋。然后似乎化简没有余地了,但是根据直觉,我们可以钦定T=kd,然后枚举T。
然后柿子变成了:
n∏T=1(∏k∣Tf[k]μ(Tk))⌊nT⌋⌊mT⌋
柿子中的∏k∣Tf[k]μ(Tk)是一个类似狄利克雷卷积的东西(取完对数就是了),可以枚举约数预处理。并且这个东西很不错的一点就是上面的指数是莫比乌斯函数,可以取到的值只有那三种,不需要快速幂。
然后剩下的部分就和通常的反演题一般无二了……
代码(卡线过的蒟蒻……求轻喷……但我在LOJ上跑的还挺快的):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | /************************************************************** Problem: 4816 User: danihao123 Language: C++ Result: Accepted Time:50840 ms Memory:33048 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <utility> #include <cctype> #include <cassert> const int N = 1000000; const int maxn = N + 5; typedef long long ll; const ll ha = 1000000007LL; bool p_flag = false ; ll pow_mod(ll a, ll b) { if (!b) return 1LL; ll ans = pow_mod(a, b / 2LL); ans = (ans * ans) % ha; if (1LL & b) ans = (ans * a) % ha; #ifdef LOCAL if (p_flag) printf ( "%lld^%lld : %lld\n" , a, b, ans); if (b == ha - 2LL) p_flag = false ; #endif return ans; } ll inv(ll x) { // if(x == 1LL) p_flag = true; return pow_mod(x, ha - 2LL); } int miu[maxn]; void sieve() { static int prm[maxn]; int cnt = 0; static bool vis[maxn]; miu[1] = 1; for ( int i = 2; i <= N; i ++) { if (!vis[i]) { miu[i] = -1; prm[cnt ++] = i; } for ( int j = 0; j < cnt && prm[j] * i <= N; j ++) { int v = prm[j] * i; vis[v] = true ; if (i % prm[j] == 0) { miu[v] = 0; break ; } else { miu[v] = miu[i] * -1; } } } } ll f[maxn], inv_d[maxn], F[maxn]; void process() { sieve(); f[1] = 1LL; inv_d[1] = 1LL; for ( int i = 2; i <= N; i ++) { f[i] = (f[i - 1] + f[i - 2]) % ha; inv_d[i] = inv(f[i]); assert (f[i] != 0LL); assert (inv_d[i] != 0LL); } for ( int i = 1; i <= N; i ++) F[i] = 1LL; for ( int i = 1; i <= N; i ++) { for ( int j = i; j <= N; j += i) { int res = j / i; if (miu[res] == 1) { F[j] = (F[j] * f[i]) % ha; #ifdef LOCAL if (j <= 3) printf ( "f[%d](%lld) -> F[%d]\n" , i, f[i], j); #endif continue ; } if (miu[res] == -1) { F[j] = (F[j] * inv_d[i]) % ha; #ifdef LOCAL if (j <= 3) printf ( "inv_f[%d](%lld) -> F[%d]\n" , i, inv_d[i], j); #endif } } } F[0] = 1LL; bool flag = true ; for ( int i = 1; i <= N; i ++) { F[i] = (F[i] * F[i - 1]) % ha; if (flag && F[i] == 0LL) { printf ( "SF[%d] has been 0.\n" , i); flag = false ; } } } ll calc(ll n, ll m) { if (n > m) std::swap(n, m); ll ans = 1LL; for (ll i = 1; i <= n;) { ll nx = std::min(n / (n / i), m / (m / i)); #ifdef LOCAL // printf("nx of %lld : %lld\n", i, nx); #endif ll mulv = pow_mod((F[nx] * inv(F[i - 1])) % ha, (n / i) * (m / i)); ans = (ans * mulv) % ha; i = nx + 1LL; } return ans; } int main() { process(); int T; scanf ( "%d" , &T); while (T --) { int n, m; scanf ( "%d%d" , &n, &m); printf ( "%lld\n" , calc(n, m)); } return 0; } |
[BZOJ 3992][SDOI2015]序列统计
终于调过去了……(然而不就是道NTT+生成函数水题吗至于调半天)
首先积非常的恶心,考虑转成和。这个事需要求指标的……求指标的话枚举原根的若干次幂即可(恰好m是素数一定有原根……),判断原根用比较大力的手段即可(我搞了一个O(n√nlogn)的……求轻喷)。
然后这题还算比较简单吧……用生成函数表示原来的集合,然后n次幂就可以了。
注意那事个循环卷积……所以要开两倍然后每次乘完了再把右半部分搞过去。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | /************************************************************** Problem: 3992 User: danihao123 Language: C++ Result: Accepted Time:6652 ms Memory:1864 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <cstdlib> #include <cctype> #include <algorithm> #include <queue> #include <utility> #include <vector> #include <cmath> typedef long long ll; const int maxn = 32005; const ll ha = 1004535809LL; const ll bs = 3LL; ll n, m, gl; int sz; ll pow_mod(ll a, ll b, ll p) { if (!b) return 1LL; ll ans = pow_mod(a, b >> 1, p); ans = (ans * ans) % p; if (b & 1LL) ans = (ans * a) % p; return ans; } ll inv(ll x, ll p) { return pow_mod(x, p - 2LL, p); } void break_up(ll x, std::vector<ll> &V) { int bd = sqrt (x + 0.5); for (ll i = 2; i <= bd; i ++) { if (x % i == 0) { V.push_back(i); while (x % i == 0) x /= i; } if (x == 1LL) break ; } if (x > 1LL) V.push_back(x); } ll get_phi() { if (m == 2LL) return 1LL; ll mi = m - 1LL; std::vector<ll> V; break_up(mi, V); for (ll i = 2; i <= mi; i ++) { bool ok = true ; for ( int j = 0; j < V.size(); j ++) { ll b = mi / V[j]; if (pow_mod(i, b, m) == 1LL) { ok = false ; #ifdef LOCAL printf ( "%lld not passed!\n" , i); #endif break ; } } if (ok) { return i; } } } int bi, len; int flip( int x) { int ans = 0; for ( int i = 0; i < bi; i ++) { if ((1 << i) & x) { ans += (1 << (bi - i - 1)); } } return ans; } void ntt(ll *A, bool flag = false ) { for ( int i = 0; i < len; i ++) { int v = flip(i); if (v < i) std::swap(A[i], A[v]); } for ( int L = 1; L < len; L <<= 1) { ll xi_n = pow_mod(3LL, (ha - 1LL) / (ll(L << 1)), ha); if (flag) xi_n = inv(xi_n, ha); for ( int i = 0; i < len; i += (L << 1)) { ll w = 1; for ( int j = i; j < i + L; j ++) { ll p1 = A[j], p2 = A[j + L]; A[j] = (p1 + (p2 * w) % ha) % ha; A[j + L] = (p1 - (p2 * w) % ha + ha) % ha; w = (w * xi_n) % ha; } } } } void poly_mul(ll *A, ll *B) { static ll C[maxn]; memset (C, 0, sizeof (C)); #ifdef LOCAL printf ( "A :" ); for ( int i = 0; i < len; i ++) printf ( " %lld" , A[i]); puts ( "" ); printf ( "B :" ); for ( int i = 0; i < len; i ++) printf ( " %lld" , B[i]); puts ( "" ); #endif ntt(A); ntt(B); for ( int i = 0; i < len; i ++) C[i] = (A[i] * B[i]) % ha; ntt(C, true ); ll inv_n = inv(len, ha); for ( int i = 0; i < len; i ++) { C[i] = (C[i] * inv_n) % ha; } #ifdef LOCAL printf ( "C (not processed) :" ); for ( int i = 0; i < len; i ++) printf ( " %lld" , C[i]); puts ( "" ); #endif for ( int i = 0; i < len; i ++) { int v = i % (m - 1LL); if (v != i) { C[v] = (C[v] + C[i]) % ha; C[i] = 0LL; } } #ifdef LOCAL printf ( "C :" ); for ( int i = 0; i < len; i ++) printf ( " %lld" , C[i]); puts ( "" ); #endif std::copy(C, C + len, A); } void poly_pow(ll *A, ll b) { static ll B[maxn]; static ll res[maxn]; std::copy(A, A + len, res); std::fill(A, A + len, 0); A[0] = 1LL; #ifdef LOCAL printf ( "A :" ); for ( int i = 0; i < len; i ++) printf ( " %lld" , A[i]); puts ( "" ); printf ( "res : " ); for ( int i = 0; i < len; i ++) printf ( "%lld " , res[i]); puts ( "" ); #endif while (b) { if (1LL & b) { std::copy(res, res + len, B); poly_mul(A, B); std::fill(B, B + len, 0); } std::copy(res, res + len, B); poly_mul(res, B); std::fill(B, B + len, 0); b >>= 1LL; } } int main() { static bool vis[maxn]; static ll A[maxn]; scanf ( "%lld%lld%lld%d" , &n, &m, &gl, &sz); ll phi = get_phi(); for ( int i = 1; i <= sz; i ++) { int v; scanf ( "%d" , &v); if (v == 0) continue ; vis[v] = true ; } int logx = 0; bi = 0; len = 1; while (len <= (2 * m - 2)) { bi ++; len <<= 1; } for ( int i = 0; i < (m - 1); i ++) { int v = pow_mod(phi, i, m); if (vis[v]) { A[i] ++; #ifdef LOCAL printf ( "log(%d) : %d\n" , v, i); #endif } if (v == gl) { logx = i; } } poly_pow(A, n); printf ( "%lld\n" , A[logx]); return 0; } |
[坑]狄利克雷卷积和反演
开个坑,记录一些和反演以及狄利克雷卷积的东西。
首先积性函数、狄利克雷卷积等基本概念不写了,就讲讲性质吧。
有几个一定要记住的东西:
μ∗1=e
φ∗1=id
μ∗id=φ
这几个在推式子的过程中都有很大的作用,务必要记住。
所谓莫比乌斯反演,其实就是:
F=f∗1⇔f=F∗μ
(谜之音:其实很多所谓“反演题”都没用到这俩性质啊……)
关于莫比乌斯函数本身,还有一个好康的性质:
(μ∗1)(k)=k∑i=0(−1)iCik
[BZOJ 2186]沙拉公主的困惑
这个题啊……亦可赛艇!
答案是。原因很简单,把
分成长度为
的若干段,除去第一段外每一段中与
互质的数
肯定满足
(否则,
和
就会有大于一的公因子了)。所以说每一段内与
互质的数都有
个。
麻烦好像就在于求一个阶乘的欧拉函数。考虑一个新乘上的数能给答案带来的贡献——如果这个数是合数,它的所有因子在前面都有了,只能把他自己贡献出来;如果这个数是质数(假设为),出了贡献自己以外还会贡献一个
,最后乘起来就是贡献了
。筛一遍素数再递推一下就好辣~
并且……可能非常大,所以说除去
那块要用逆元做。
(顺便说下阶乘也要递推)
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | /************************************************************** Problem: 2186 User: danihao123 Language: C++ Result: Accepted Time:9408 ms Memory:166836 kb ****************************************************************/ #include <cstdio> #include <cmath> typedef unsigned long long ull; const int maxn=10000000; ull R; bool vis[maxn+5]; inline void sievePrime(){ register int i,j,m= sqrt (maxn+0.5); for (i=2;i<=m;i++) if (!vis[i]) for (j=i*i;j<=maxn;j+=i) vis[j]= true ; } ull fac[maxn+5]; inline void sieveFac(){ register int i; fac[0]=1%R; for (i=1;i<=maxn;i++) fac[i]=(fac[i-1]*(i%R))%R; } ull phifac[maxn+5]; inline void sievePhiFac(){ register int i; phifac[1]=1%R; for (i=2;i<=maxn;i++){ if (vis[i]) phifac[i]=(phifac[i-1]*(i%R))%R; else phifac[i]=(phifac[i-1]*((i%R-1%R+R)%R))%R; } } void exgcd(ull a,ull b,ull& d,ull& x,ull& y){ if (!b){ d=a; x=1; y=0; } else { exgcd(b,a%b,d,y,x); y-=x*(a/b); } } ull inv(ull a){ ull d,x,y; exgcd(a,R,d,x,y); return (x+R)%R; } int main(){ int T; int n,m; scanf ( "%d%llu" ,&T,&R); sievePrime(); sieveFac(); sievePhiFac(); while (T--){ scanf ( "%d%d" ,&n,&m); printf ( "%llu\n" ,(phifac[m]*((fac[n]*inv(fac[m]))%R))%R); } return 0; } |
[BZOJ 2118]墨墨的等式
论如何把数论乱搞和图论乱搞出在一起……
这个题由于要求,所以不能gcd乱搞。我们可以先取
的最小值
(忽略为0的情况,为啥取最小值待会再说),对方程两边模
。然后对于任何能使某个同余方程成立的
,将其中所有
同时加任意个
,同余方程都成立。
取模后,,所以说只要对于
中的每个数找出最小的一组合法解即能推出其他解(所以说,剩余系越少效率越高,这也就要求取的
要小)。不过这个最小的一组合法解怎么找?
我们先找出任意一个合法(比如说0吧),然后尝试加上
,就可以推出其他
的最小解。这个应用当然是需要最短路辣。
求出来的最短路,表示的是取最小解时的。这样的话就可以推出某个前缀区间中合法
的个数(能加多少
,就有多少个,注意不要忽略加0个的情况),并且答案符合区间减法,最后做差分即可。
注意忽略的情况(相当于不存在)。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | /************************************************************** Problem: 2118 User: danihao123 Language: C++ Result: Accepted Time:1952 ms Memory:5508 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <queue> #include <algorithm> #ifdef DEBUG #include <cassert> #endif using namespace std; typedef long long ll; const int maxn=15; const ll INF=0x7f7f7f7f7f7f7f7f; ll A[maxn]; bool inQueue[500005]; ll d[500005]; int n; ll minv; inline void SPFA(){ register int i,u,to; queue< int > Q; memset (d,0x7f, sizeof (d)); d[0]=0; Q.push(0); inQueue[0]= true ; #ifdef DEBUG assert (d[1]==INF); #endif while (!Q.empty()){ u=Q.front(); Q.pop(); inQueue[u]= false ; for (i=1;i<=n;i++){ to=(u+A[i])%minv; if (d[u]<INF && d[u]+A[i]<d[to]){ d[to]=d[u]+A[i]; if (!inQueue[to]){ Q.push(to); inQueue[to]= true ; } } } } } inline ll Calc(ll x){ register ll ans=0; register int i=0; for (i=0;i<minv;i++) if (d[i]<=x) ans+=(x-d[i])/minv+1; return ans; } int main(){ ll l,r; register int i; scanf ( "%d%lld%lld" ,&n,&l,&r); minv=0x7fffffff; for (i=1;i<=n;i++){ scanf ( "%d" ,&A[i]); if (!A[i]){ i--; n--; continue ; } minv=min(minv,A[i]); } SPFA(); printf ( "%lld\n" ,Calc(r)-Calc(l-1)); return 0; } |
[BZOJ 2190]仪仗队
这个题啊……有规律可循。
我们可以发现,关于答案有如下规律:
然后筛法求欧拉函数即可(我听说神犇们都用了杜教筛哎)
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | /************************************************************** Problem: 2190 User: danihao123 Language: C++ Result: Accepted Time:28 ms Memory:976 kb ****************************************************************/ #include <cstdio> #include <algorithm> using namespace std; const int maxn=40005; #define CUS_REP(i,a,b) for(i=(a);i<=(b);i++) int phi[maxn]; int n; void sieve(){ register int i,j; phi[1]=1; CUS_REP(i,2,n) if (!phi[i]) for (j=i;j<=n;j+=i){ if (!phi[j]) phi[j]=j; phi[j]=phi[j]/i*(i-1); } } int main(){ register int i,ans=2; scanf ( "%d" ,&n); sieve(); /* #ifdef DEBUG CUS_REP(i,1,n) printf("%d\n",phi[i]); #endif */ CUS_REP(i,2,n-1) ans+=phi[i]; printf ( "%d\n" ,ans*2-1); return 0; } |
[BZOJ 1441]Min
这个问题看似无从下手。
我们可以先取,然后你就发现你似乎要解
,而且还要求
最小?你想到了什么?扩展欧几里得?对头!
由扩欧推广可得答案就是所有数的最大公约数。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | /************************************************************** Problem: 1441 User: danihao123 Language: C++ Result: Accepted Time:0 ms Memory:820 kb ****************************************************************/ # include <cstdio> # include <cmath> # include <algorithm> using namespace std; int gcd( int a, int b){ return b== 0 ?a:gcd(b,a%b); } int main(){ register int ans,i; int n,temp; scanf( "%d" ,&n); scanf( "%d" ,&temp); ans=abs(temp); for (i= 2 ;i<=n;i++){ scanf( "%d" ,&temp); ans=gcd(ans,abs(temp)); } printf( "%d\n" ,ans); return 0 ; } |
[CF 371B]Fox Dividing Cheese
狐狸的三种操作的实质是除二,除三,除五。由此我们可以猜想在最优策略下,两块蛋糕最后的重量应该是。然后试除即可。
(大胆猜想,不用证明)
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | #include <iostream> #include <algorithm> using namespace std; int gcd( int a, int b){ if (!b) return a; else return gcd(b,a%b); } static int P[3]={2,3,5}; inline int min_fj( int source, int direction){ register int i,ans=0; source/=direction; if (source==1) return 0; for (i=2;i>=0;i--){ while (source%P[i]==0){ source/=P[i]; ans++; } } if (source==1) return ans; else return -1; } int main(){ register int direction,t1,t2; int a,b; cin>>a>>b; if (a==b){ cout<<0<<endl; return 0; } direction=gcd(a,b); t1=min_fj(a,direction); if (t1==-1){ cout<<-1<<endl; return 0; } t2=min_fj(b,direction); if (t2==-1){ cout<<-1<<endl; return 0; } cout<<t1+t2<<endl; return 0; } |
[CodeVS 1012]最大公约数和最小公倍数问题
很经典的问题了吧……然而现在才A……
应注意,然而
和
都可表示为
的乘积,问题就好思考多了。答案就是
的质因子的子集数(同一种质因子不能同时分配到P与Q中,否则gcd(P,Q)会不等于x)。
注意有可能无解!
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <iostream> using namespace std; inline int fj( int x){ register int i,ans=0; for (i=2;i<=x && x>1;i++) if (!(x%i)){ ans++; while (!(x%i)) x/=i; } return ans; } int main(){ int a,b; register int ans; cin>>a>>b; if (b%a) ans=0; else ans=a==1?1:1<<fj(b/a); cout<<ans<<endl; return 0; } |