1. 程式人生 > >2018 Multi-University Training Contest 6

2018 Multi-University Training Contest 6

聯通 push_back .net ++ 質數 ipa 合數 acm can

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

2018 Multi-University Training Contest 6