2018 Multi-University Training Contest 6

Werewolf

只考虑村民边,然后每个村民边求联通块,最后加狼边,看看两个端点在不在一个联通块里就可以了。 比赛的时候有点坑,思路正确,写歪了……

#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\n", ans);
    }
    return 0;
}
/*423
6
2 v
3 v
4 v
5 v
3 w
4 v
*/
Code

bookshelf

这题首先要找到规律:

$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)\times 2^{a-b},2^{b}-1)$ (这个和正常的辗转相除一样:$gcd(a,b)=gcd(a-b\times \lfloor a / b\rfloor , b)$)

整理得:

$=gcd(2^{a-b}-1,2^{b}-1)$

继续:

$=gcd(2^{a-2\times 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\n", ans);
    }
    return 0;
}
Code

sacul

Shoot Game

 Ringland

Variance-MST

猜你喜欢

转载自www.cnblogs.com/littlepear/p/9454530.html