1. 程式人生 > >湖南測試 3

湖南測試 3

set cstring 思路 memset power etl 倒序 組合 can

技術分享

技術分享

技術分享

思路:對於第一問:f[i][j]表示前i個數,當前黑板上的數為j的概率

當前有三種情況

  1. 當前數不是j的倍數—>黑板上的數字改變。

  2. 當前數是j的倍數且當前數在前i個數中(已經選過)

  3. 當前數是j的倍數且沒有選過

轉移:f[i+1][j]=((j的倍數個數-i)*f[i][j]+f[i][gcd(j,k)])的平均值 j的倍數個數-i是沒選過的j的倍數。

對於第二問,考慮博弈論中sg函數。可知sg[i][1]二維含義同f數組)必定為0(最後黑板上剩下1必敗) sg[n][i]=0(選完了必敗) 同樣枚舉上述三種情況,取後續狀態mex

值即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define drep(i, r, l) for (int i = r; i >= l; i--)
typedef long long ll;
const int N = 1008;
int n, T, a[N], g[N][N], sg[N][N], m, cnt[N], mex[N];
double f[N][N]; int gcd(int x, int y) {return !y ? x : gcd(y, x % y);} void init(int n) { rep(i, 0, n) rep(j, 0, n) g[i][j] = gcd(i, j); } bool check() { int gg=0; for (int i=1; i<=n; i++) gg=g[gg][a[i]]; if (gg==1) return false; if (n&1) printf("%.9lf 1.000000000\n",1);
else printf("%.9lf 0.000000000\n",0); return true; } void solve() { if (check()) return ; memset(sg, 0, sizeof(sg)); memset(f, 0, sizeof(f)); memset(mex, 0, sizeof(mex)); rep(j, 0, m) sg[n][j] = 0, f[n][j] = 0; sg[n][1] = 1,f[n][1] = 1; int p = 0; drep(i, n - 1, 0) rep(x, 0, m) { if (x == 1) { sg[i][x] = 1; f[i][x] = 1; continue; } memset(cnt, 0, sizeof(cnt)); rep(j, 1, n) cnt[g[x][a[j]]]++; if (x && i && cnt[x] < i) continue; if (x) cnt[x] -= i; ++p; rep(y, 0, m) {if (cnt[y]) mex[sg[i + 1][y]] = p, f[i][x] += (double)cnt[y] / (n - i) * (1 - f[i + 1][y]);} for (sg[i][x] = 0; mex[sg[i][x]] == p; sg[i][x]++); } if (sg[0][0]) printf("%.9lf 1.000000000\n", f[0][0]); else printf("%.9lf 0.000000000\n", f[0][0]); } int main() { freopen("cards.in", "r", stdin); freopen("cards.out", "w", stdout); init(1000); scanf("%d", &n); m = 0; rep(i, 1, n) scanf("%d", &a[i]), m = max(m, a[i]); solve(); fclose(stdin); fclose(stdout); return 0; }

技術分享

技術分享

思路:T2樹形dp+LCA

對於60~80分,可以n^2暴力,斷掉每條邊時,O(N)求每個部分的直徑,然後相乘。

正解:倒序加邊,考慮兩棵樹合並的時候新直徑一定是原來兩個直徑四個斷點任意兩個的路徑。所以可以首先求LCA倍增處理兩點間路徑,然後求最大。

#include <cstdio>
#include <iostream>
#define N 111111
#define MOD 1000000007
#define LL long long
using namespace std;
int a[N], dep[N], sum[N], par[N][18], h[N], del[N];
int endpoint[N][2], ans[N], f[N], length[N];
int tote, n, product;
struct edge {
    int s, t, n;
} e[N * 2];
void adde(int u, int v) {
    e[++tote].t = v;
    e[tote].s = u;
    e[tote].n = h[u];
    h[u] = tote;
    return ;
}
void dfs(int u, int fa) {
    par[u][0] = fa;
    dep[u] = dep[fa] + 1;
    sum[u] = sum[fa] + a[u];
    for (int i = 1; i < 18; i++) par[u][i] = par[par[u][i - 1]][i - 1];
    for (int i = h[u]; i; i = e[i].n) {
        int v = e[i].t;
        if (v != fa) dfs(v, u);
    }
    return ;
}
int lca(int u, int v) {
    if (dep[u] < dep[v]) swap(u, v);
    for (int t = dep[u] - dep[v], i = 0; t > 0; t >>= 1, i++)
        if (t & 1) u = par[u][i];
    int t = 17;
    while (u != v) {
        while (t && par[u][t] == par[v][t]) t--;
        u = par[u][t];
        v = par[v][t];
    }
    return u;
}
int getf(int u) {
    if (u == f[u]) return u;
    f[u] = getf(f[u]);
    return f[u];
}
int pw(int a, int b) {
    int ans = 1, t = a;
    for (int i = b; i; i >>= 1) {
        if (i & 1) ans = (LL) ans * t % MOD;
        t = (LL) t * t % MOD;
    }
    return ans;
}
int getlength(int u, int v) {
    int w = lca(u, v);
    return sum[u] + sum[v] - 2 * sum[w] + a[w];
}
int getint() {
    char ch;
    do {ch=getchar();}
    while (ch!=-&&(ch<0||ch>9));
    int ans=0,f=0;
    if (ch==-) f=1;
    else ans=ch-0;
    while (isdigit(ch=getchar())) ans=ans*10+ch-0;
    if (f) ans*=-1;
    return ans;
}
int main() {
    freopen("forest.in", "r", stdin);
    freopen("forest.out", "w", stdout);
    n = getint();
    product = 1;
    for (int i = 1; i <= n; i++) {
        a[i] = getint();
        f[i] = i;
        product = (LL) product * a[i] % MOD;
        endpoint[i][0] = endpoint[i][1] = i;
        length[i] = a[i];
    }
    for (int i = 1; i < n; i++) {
        int u = getint(), v = getint();
        adde(u, v);
        adde(v, u);
    }
    dfs(1, 0);
    int t = n;
    ans[t] = product;
    for (int i = 1; i < n; i++) del[i] = getint();
    for (int i = n - 1; i; i --) {
        int id = del[i], u = e[id * 2 - 1].s, v = e[id * 2 - 1].t;
        u = getf(u);
        v = getf(v);
        if (length[u] < length[v]) swap(u, v);
        int tmax = length[u], end[2];
        for (int j = 0; j < 2; j++) end[j] = endpoint[u][j];
        for (int j = 0; j < 2; j++)
            for (int k = 0; k < 2; k++) {
                int l = getlength(endpoint[u][j], endpoint[v][k]);
                if (l > tmax) {
                    tmax = l;
                    end[0] = endpoint[u][j];
                    end[1] = endpoint[v][k];
                }
            }
        product = (LL) product * pw(length[u], MOD - 2) % MOD;
        product = (LL) product * pw(length[v], MOD - 2) % MOD;
        f[v] = u;
        length[u] = tmax;
        for (int j = 0; j < 2; j++) endpoint[u][j] = end[j];
        product = (LL) product * length[u] % MOD;
        ans[--t] = product;
    }
    for (int i = 1; i <= n; i++) printf("%d\n", ans[i]);
    return 0;
}

技術分享

技術分享

思路:

組合數學

考慮兩列的情況。若兩列顏色分別為A,B,A獨有的顏色就是AAB B同理。

若是多列那還是設兩邊兩列為A,B,中間多列為C,那根據題目結論可以知道C一定是AB的子集。枚舉AB中共有顏色個數iB中獨有顏色個數與A中相同為j。有C(Ki)*C(k-i+j)*C(k-i-jj)//共有+A,B,獨有的情況。然後再求C中的情況,因為每個塊都有染每種顏色的概率,所以總的就是k^n(m-2)種。但要保證這k種顏色必須都用,所以減掉那些用了<k種的就是最後答案。

#include<cstdio>
#include<iostream>
using namespace std;
const int pp=1000000007;
int c[2008][2008],f[2008],p[2008],ni[2008];
int n,m,k,nn;
inline int power(int x,int n) {
    int ans=1,tmp=x;
    while (n) {
        if (n&1) ans=(long long)ans*tmp%pp;
        tmp=(long long)tmp*tmp%pp;
        n>>=1;
    }
    return ans;
}
void Count_c() {
    for (int i=0; i<=nn; i++) c[i][0]=1;
    for (int i=1; i<=nn; i++)
        for (int j=1; j<=i; j++) {
            c[i][j]=c[i-1][j-1]+c[i-1][j];
            if (c[i][j]>=pp) c[i][j]-=pp;
        }
}
void Count_p() {
    int mm=(m-2)*n;
    for (int i=0; i<=nn; i++)
        p[i]=power(i,mm);
}
void Count_f() {
    f[0]=0;
    f[1]=1;
    for (int i=2; i<=nn; i++) {
        f[i]=power(i,n);
        for (int j=1; j<i; j++) {
            f[i]-=(long long)f[j]*c[i][j]%pp;
            if (f[i]<=-pp) f[i]+=pp;
        }
        if (f[i]<0) f[i]+=pp;
    }
}
void Count_ni() {
    ni[1]=1;
    for (int i=2; i<=nn; i++)
        ni[i]=power(i,pp-2);
}
int main() {
    freopen("photo.in","r",stdin);
    freopen("photo.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    nn=min(n,k);
    if (m==1)
        printf("%d\n",power(k,n));
    else {
        Count_c();
        Count_p();
        Count_f();
        Count_ni();
        long long tmp=1,tmp1=1,sum=0,sum1;
        for (int s=1; s<=nn; s++) {
            tmp=tmp*ni[s]%pp;
            tmp=tmp*(k-s+1)%pp;
            tmp1=1;
            sum1=0;
            for (int j=0; j<=s; j++) {
                sum1+=tmp1*c[s][s-j]%pp*p[s-j]%pp;
                if (sum1>=pp) sum1-=pp;
                tmp1=tmp1*ni[j+1]%pp;
                if (k-s<j+1) break;
                tmp1=tmp1*(k-s-j)%pp;
            }
            sum+=tmp*f[s]%pp*f[s]%pp*sum1%pp;
            if (sum>=pp) sum-=pp;
        }
        printf("%d\n",sum);
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

湖南測試 3