2018 Multi-University Training Contest 6
Posted littlepear
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2018 Multi-University Training Contest 6相关的知识,希望对你有一定的参考价值。
只考虑村民边,然后每个村民边求联通块,最后加狼边,看看两个端点在不在一个联通块里就可以了。 比赛的时候有点坑,思路正确,写歪了……
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 50;
vector<int> g[maxn];
int fa[maxn];
int in[maxn];
int lang[maxn];
int cnt[maxn];
void dfs(int u, int root) ///同属一个根
{
fa[u] = root;
for(int i = 0; i < g[u].size(); i++)
{
dfs(g[u][i], root);
cnt[u] += cnt[g[u][i]];
}
}
int main()
{
int T; scanf("%d", &T);
while(T--)
{
int n; scanf("%d", &n);
for(int i = 1; i <= n; i++) g[i].clear(), fa[i] = 0, in[i] = 0, cnt[i] = 1, lang[i] = 0;
for(int i = 1; i <= n; i++)
{
int v;
char s[15];
scanf("%d %s", &v, s);
if(s[0] == ‘v‘)
{
g[v].push_back(i);
in[i]++;
}
else lang[i] = v;
}
for(int i = 1; i <= n; i++) if(!in[i]) dfs(i, i);
int ans = 0;
for(int i = 1; i <= n; i++)
{
if(!in[i] && (fa[lang[i]] == i)) ans += cnt[lang[i]];
}
printf("0 %d
", ans);
}
return 0;
}
/*423
6
2 v
3 v
4 v
5 v
3 w
4 v
*/
这题首先要找到规律:
$gcd(2^{a}-1,2^{b}-1)=2^{gcd(a,b)}-1.$
这个看大家都没有证明,我看了dls的讲解后,还有问学长,给出下面证明:
设$a < b;$
$gcd(2^{a}-1,2^{b}-1)$
$=gcd(2^{a}-1-(2^{b}-1) imes 2^{a-b},2^{b}-1)$ (这个和正常的辗转相除一样:$gcd(a,b)=gcd(a-b imes lfloor a / b floor , b)$)
整理得:
$=gcd(2^{a-b}-1,2^{b}-1)$
继续:
$=gcd(2^{a-2 imes b}-1,2^{b}-1)$
……
$=gcd(2^{a\\%b}-1,2^{b}-1)$
然后这时候$a\\%b < b$,再像正常的那样反过来继续$gcd$就行,直到一边$a\\%b=0,2^{0}-1=0,$那么最后的$gcd$就是$2^{g}-1$,而$g=gcd(a,b)$,证明完毕。
而$gcd(fib(a),fib(b))=fib(gcd(a,b))$,这位老铁给出了证明。
而最后化简完求的是:$2^{gcd(f[x1],f[x2],……,f[xk])}=2^{f[gcd(x1,x2,……,xk)]}。$
令$d=gcd(x1,x2,……,xk),$相当于$d|x1,d|x2,……,d|xk.$
对$x1,x2,……,xk$求和,得$frac{x1+x2+……+xk=n}{d}.$ 问题演化为:有k个数,每个数都可以除以d,并且k个数的和是n,问这样的数的组合有多少种?
利用隔板法:可以先把n分成n/d份,然后得出组合数$C(n/d+k-1,k-1)$。
这时,我们令f[d]表示 $d | gcd(x1,x2,……,xk)$的方案数。
令g[d]表示$d=gcd(x1,x2,……,xk)$的方案数。
则$f[d]=C(n/d+k-1,k-1)$。
而$g[d]=f[d]-sum_{d|x} g[x]$,从大到小枚举因子就可以了。
不太懂f[d]和g[d]的关系,我们可以举个例子,假如$n=12,k=3,$首先枚举d=1,那么f[d]不仅包括$gcd=1$的,还包括$gcd=2、3、4、6、12$的,所以我们只算$gcd=1$的,减去其他的就可以了,最后答案再乘以$2^{fib[d]}-1$就可以了。
因为指数特别大,最后再利用欧拉降幂公式 不要求a和n互质。
这里n是质数,$a^{varphi(n)} \\% n=1$,根据费马小定理,所以可以不用加后面。
#include <bits/stdc++.h> using namespace std; const int maxn = 2e6 + 50; typedef long long ll; int fac[maxn]; int sing[maxn]; int inv[maxn]; int f[maxn / 2]; int g[maxn / 2]; int fib[maxn / 2]; const ll mod = 1e9 + 7; void init(int n) { fac[0] = fac[1] = sing[0] = sing[1] = inv[0] = inv[1] = 1; for(int i = 2; i <= n; i++) { fac[i] = (ll)fac[i - 1] * i % mod; sing[i] = (ll)(mod - mod / i) * sing[mod % i] % mod; inv[i] = (ll)inv[i - 1] * sing[i] % mod; } } ll quick_pow(ll a,ll n) { ll res = 1; while(n) { if(n % 2LL) res = res * a % mod; n >>= 1LL; a = a * a % mod; } return res; } vector<int> yue; int main() { duipai(); init(2e6 + 10); int T; scanf("%d", &T); fib[0] = 0, fib[1] = fib[2] = 1; ll fain = mod - 1; int early = maxn; for(int i = 3; i <= (1e6 + 5); i++) { fib[i] = fib[i - 1] + fib[i - 2]; if((ll)fib[i] > fain) { if(maxn == early) early = i; fib[i] = (ll)fib[i] % fain; } } while(T--) { yue.clear(); int n, k; scanf("%d %d", &n, &k); for(int i = 1; i <= n; i++) { f[i] = 0; g[i] = 0; if(n % i == 0) { yue.push_back(i); f[i] = (ll)fac[n / i + k - 1] * inv[k - 1] % mod * (ll)inv[n / i] % mod; } } ll ans = 0; for(int i = yue.size() - 1; i >= 0; i--) { int d = yue[i]; g[d] = f[d]; for(int j = i + 1; j < yue.size(); j++) { int x = yue[j]; if(x % d == 0) g[d] = ((ll)g[d] + mod - (ll)g[x]) % mod; } } for(int i = 0; i < yue.size(); i++) { int d = yue[i]; ll mi = fib[d] + (d >= early ? fain : 0); ans = (ans + (ll)g[d] * ((quick_pow(2LL, mi) - 1LL + mod) % mod) % mod) % mod; } ans = (ans * quick_pow((ll)f[1], mod - 2)) % mod; printf("%lld ", ans); } return 0; }
以上是关于2018 Multi-University Training Contest 6的主要内容,如果未能解决你的问题,请参考以下文章
2018 Multi-University Training Contest 2
2018 Multi-University Training Contest 9
2018 Multi-University Training Contest 4
2018 Multi-University Training Contest 4