暑假集训6

A. 接力比赛

TLE:把能力值看成一个背包,为了恰好把背包装满,我还拿了另外两个数组判断能不能恰好装满,TLE 40,但其实这个判断是没用的,初始化负无穷不能装满的根本不可能作为一个比较优的解来更新答案,把它删了就是TLE 70……

但还是会TLE,因为每次都从最大的循环太多余了,从前缀和开始就好,因为能凑出的范围不可能比前缀和还大。为了少循环一些东西,还可以sort一下,好像真的比不sort快。(and这题随机化也可以过,或者说第二层循环的上限改成3.5e5,方法非常多……)

#include <bits/stdc++.h>
  
using namespace std;
  
typedef long long ll;
const int maxn = 1003;
const int N = 1e6 + 2;
const ll mod = 1e9 + 7;

int n, m;
ll s1, s2, W, ans, dp1[N], dp2[N];
bool f1[N], f2[N];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct node
{
    ll w, v;
}p[maxn], q[maxn];

int main()
{
    freopen("game.in", "r", stdin);
    freopen("game.out", "w", stdout);
    
    n = read(); m = read();
    for(int i=1; i<=n; i++)
    {
        p[i].w = read(); p[i].v = read();
        s1 += p[i].w;
    }
    for(int i=1; i<=m; i++)
    {
        q[i].w = read(); q[i].v = read();
        s2 += q[i].w;
    }
    W = min(s1, s2);
    for(int i=1; i<=W; i++)
    {
        dp1[i] = dp2[i] = -1e12;
    }
    f1[0] = 1;
    for(int i=1; i<=n; i++)
    {
        for(int j=W; j>=p[i].w; j--)
        {
            f1[j] = f1[j] | f1[j-p[i].w];
            if(f1[j-p[i].w])
            {
                dp1[j] = max(dp1[j], dp1[j-p[i].w]+p[i].v);
            }
        }
    }
    f2[0] = 1;
    for(int i=1; i<=m; i++)
    {
        for(int j=W; j>=q[i].w; j--)
        {
            f2[j] = f2[j] | f2[j-q[i].w];
            if(f2[j-q[i].w])
            {
                dp2[j] = max(dp2[j], dp2[j-q[i].w]+q[i].v);
            }
        }
    }
    for(int i=1; i<=W; i++)
    {
        if(f1[i] && f2[i])
        {
            ans = max(ans, dp1[i]+dp2[i]);
        }
    }
    printf("%lld\n", ans);
  
    return 0;
}
TLE 40
#include <bits/stdc++.h>
  
using namespace std;
  
typedef long long ll;
const int maxn = 1003;
const int N = 1e6 + 2;
const ll mod = 1e9 + 7;

int n, m;
ll s1, s2, W, ans, dp1[N], dp2[N], W1, W2;
bool f1[N], f2[N];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct node
{
    ll w, v;
    bool operator < (const node &T) const 
    {
        if(w == T.w) return v < T.v;
        return w < T.w;
    }
}p[maxn], q[maxn];

int main()
{
    freopen("game.in", "r", stdin);
    freopen("game.out", "w", stdout);
    
    n = read(); m = read();
    for(int i=1; i<=n; i++)
    {
        p[i].w = read(); p[i].v = read();
    }
    for(int i=1; i<=m; i++)
    {
        q[i].w = read(); q[i].v = read();
    }
    sort(p+1, p+1+n);
    sort(q+1, q+1+m);
    memset(dp1, -64, sizeof(dp1));
    memset(dp2, -64, sizeof(dp2));
    dp1[0] = dp2[0] = 0;
    for(int i=1; i<=n; i++)
    {
        s1 += p[i].w;
        for(int j=s1; j>=p[i].w; j--)
        {
            dp1[j] = max(dp1[j], dp1[j-p[i].w]+p[i].v);
        }
    }
    for(int i=1; i<=m; i++)
    {
        s2 += q[i].w;
        for(int j=s2; j>=q[i].w; j--)
        {
            dp2[j] = max(dp2[j], dp2[j-q[i].w]+q[i].v);
        }
    }
    W = min(s1, s2);
    for(int i=1; i<=W; i++)
    {
        ans = max(ans, dp1[i]+dp2[i]);
    }
    printf("%lld\n", ans);
  
    return 0;
}
View Code AC

 

B. 树上竞技

1.大暴力,2^n枚举每个点是不是起点,去掉不合法的情况,再枚举以每个点为集合点,记录答案,注意一下数组不要按题目最大数据开,否则可能T 0。

#include <bits/stdc++.h>
  
using namespace std;
  
typedef long long ll;
const int maxn = 26;
const int N = 1e6 + 2;
const ll mod = 1e9 + 7;

int n, m, dep[maxn];
ll ans, res, ces;
bool vis[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct node 
{
    int next, to;
}a[maxn<<1];
int head[maxn], len;

void add(int x, int y)
{
    a[++len].to = y; a[len].next = head[x];
    head[x] = len;
}

inline int lowbit(int x) {return x&-x; }

int Count(int x)
{
    int k = 0;
    while(x)
    {
        x -= lowbit(x);
        k++;
    }
    return k;
}

void Color(int x)
{
    memset(vis, 0, sizeof(vis));
    int k = 1;
    while(x)
    {
        if(x & 1) vis[k] = 1;
        x >>= 1;
        k++;
    }
}

void dfs(int u, int fa, int depth)
{
    dep[u] = depth;
    for(int i=head[u]; i; i=a[i].next)
    {
        int v = a[i].to;
        if(v == fa) continue;
        dfs(v, u, depth+1);
    }
    if(vis[u]) res += dep[u];
}

int main()
{
    freopen("meeting.in", "r", stdin);
    freopen("meeting.out", "w", stdout);
    
    n = read(); m = read();
    for(int i=1; i<n; i++)
    {
        int x = read();
        add(i+1, x); add(x, i+1);
    }
    int Max = (1<<n)-1;
    for(int i=1; i<=Max; i++)
    {
        if(Count(i) != m) continue;
        //printf("New One:\n");
        //printf("i = %d\n", i);
        Color(i);
        /*for(int j=1; j<=n; j++)
        {
            if(vis[j]) printf("%d is chosen\n", j);
        }*/
        ces = 1e17;
        for(int j=1; j<=n; j++)
        {
            memset(dep, 0, sizeof(dep));
            res = 0;
            dfs(j, 0, 0);
            /*for(int k=1; k<=n; k++)
            {
                printf("dep[%d] = %d\n", k, dep[k]);
            }*/
            ces = min(ces, res);
        }
        //printf("add = %lld\n", ces);
        ans = (ans + ces) % mod;
    }
    printf("%lld\n", ans);
  
    return 0;
}
TLE 20

2.链上的情况:m是奇数,集合点一定是最中间的那个点,枚举中心点左边有多少数,要在它左边和右边分别选(m-1)/2个(实际上向下取整不用减1),设d=(m-1)/2,左边一共有C(i, d)种选法,如果把所有情况综合起来,每个点被选中的次数是相等的,每个点被选中的次数就是固定这个点必须选,备选数集合和目标选数集合各减去1的组合数,这就是每个点做贡献的次数,还需要左右合起来才是一起的贡献次数,所以要*上右边所有的情况,每个点贡献一次的贡献值就是它们的距离总和,这是一个等差数列。

所以左边=i*(i+1)/2*C(i-1, d-1)*C(n-i-1, d),再加上右边同理。

3.m=2就是计算每两个点之间的距离和,可以按边考虑,枚举每条边加上它一边的点数和另一边的点数的乘积。

4.正解:

图片上把min拆开的那个式子*2了,但其实不能*2,应该是sigma(i~(m-1)/2) C(s, i)*C(n-s, i)*i+C(s, m-i)*C(n-s, i)*i 注意第二项也是*i因为i枚举到(m-1)/2,如果i枚举到m才是m-i。后面的特判是因为除法向下取整。

把这个式子化简就成了s*sigma(i=1~k) C(s-1, i-1)*C(n-s, m-i)+(n-s)*sigma(i-1~k) C(s, m-i)*C(n-s-1, i-1),至于怎么化简就是把乘数的i和分母上的i消掉,还有s是每个式子都少乘了一个可以提出来,(乘法结合律)1*s+2*s+3*s+...=(1+2+3+...)*s  (别问我为啥解释这个……)

观察化简后式子的后半部分,如果把所有的s换成n-s,n-s换成s,就和前半部分一样,所以先只考虑前半部分:先不管*s,设f[s]=sigma(i=1~k) C(s-1, i-1)*C(n-s, m-i),最后再把s乘给f[s]。

解释f[i]到f[i+1]的递推(结合上面的题解图片):f[i+1]不能从f[i]直接转移是因为多了不合法的情况,即前面i-1个已经选够了k-1个物品的同时选上了第i个,目标是要在前i(i+1-1)个里面也选k-1个物品现在却选了k个,C(i-1, k-1)是前i-1个恰好选了k-1个物品,怎么让第i个必须被选上?那就是固定它之后的必须选m-1-(k-1)-1=m-k-1个。

一看这个大写的WR就知道我这篇代码的版权属于WintersRain大佬了%%%

//%%% WR 太巨了 这么巨 这么巨 这么巨…… %%%%%%%%%%%
#include <bits/stdc++.h>
  
using namespace std;
  
typedef long long ll;
const int maxn = 1003;
const int N = 1e6 + 2;
const ll mod = 1e9 + 7;
const int WR = 2001000;//%%%%%%%%%%

int siz[WR], n, m;
ll f[WR], g[WR], ny[WR], ans;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct node 
{
    int next, to;
}a[WR];
int head[WR], len;

void add(int x, int y)
{
    a[++len].to = y; a[len].next = head[x];
    head[x] = len;
}

ll qpow(ll a, ll b)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

void dfs(int u, int fa)
{
    siz[u] = 1;
    for(int i=head[u]; i; i=a[i].next)
    {
        int v = a[i].to;
        if(v == fa) continue;
        dfs(v, u);
        siz[u] += siz[v];
    }
}

ll C(ll n, ll m)
{
    if(m > n || m < 0 || n < 0) return 0;
    return g[n]*ny[m]%mod*ny[n-m]%mod;
}

int main()
{
    freopen("meeting.in", "r", stdin);
    freopen("meeting.out", "w", stdout);
    
    g[0] = ny[0] = 1;
    for(int i=1; i<WR; i++) g[i] = g[i-1]*i%mod;
    ny[WR-1] = qpow(g[WR-1], mod-2);
    for(int i=WR-2; i>=1; i--) ny[i] = ny[i+1]*(i+1)%mod;
    n = read(); m = read();
    for(int i=1; i<n; i++)
    {
        int x = read(); add(x, i+1); add(i+1, x);
    }
    dfs(1, 0);
    if(m % 2 == 0)
    {
        for(int i=2; i<=n; i++)
        {
            ans = (ans+C(siz[i], m/2)*C(n-siz[i], m/2)%mod*(m/2)%mod)%mod;
        }
    }
    int k = (m-1)/2;
    if(k) f[1] = C(n-1, m-1);
    for(int i=1; i<n; i++)
    {
        f[i+1] = (f[i]-C(i-1, k-1)*C(n-i-1, m-k-1)%mod+mod)%mod;
    }
    for(int i=1; i<=n; i++) f[i] = f[i]*i%mod;
    for(int i=2; i<=n; i++) ans = (ans+f[siz[i]]+f[n-siz[i]])%mod;
    printf("%lld\n", ans);
  
    return 0;
}
View Code

 

C. 虚构推理

第一想法就是枚举时间,我没想到居然还要枚举到比秒更小的,结果它WA 20,精度不够,改一下精度就成了TLE 40.

#include <bits/stdc++.h>
  
using namespace std;
  
typedef long long ll;
const int maxn = 5e4 + 3;
const int N = 1e6 + 2;
const ll mod = 1e9 + 7;

int n;
double ans = mod*1.0;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct node 
{
    int x, y, z;
    double nt, ct;
}p[maxn];

int main()
{
    freopen("unreal.in", "r", stdin);
    freopen("unreal.out", "w", stdout);
    
    n = read();
    for(int i=1; i<=n; i++)
    {
        p[i].x = read(); p[i].y = read(); p[i].z = read();
        if(p[i].x >= 12) p[i].x -= 12;
        p[i].ct = p[i].y*6.0+p[i].z*0.1;
        p[i].nt = p[i].x*30.0+p[i].ct/12.0;
        //printf("main : %.6lf %.6lf\n", p[i].ct, p[i].nt);
    }
    bool f = 1;
    for(int x=0; x<12; x++)
    {
        double nt1 = x*30.0;
        for(int y=0; y<60; y++)
        {
            double ct1 = y*6.0;
            //double ct2 = y*0.5;
            for(double z=0; z<60; z+=0.05)
            {
                double t1 = z*0.1;
                //double t2 = z*0.05/6.0;
                double ct = ct1+t1, nt = nt1+ct/12.0;
                //printf("Cat: %.6lf %.6lf\n", ct, nt);
                double c1 = 0.0, c2 = 0.0;
                for(int i=1; i<=n; i++)
                {
                    c1 = max(c1, min(fabs(p[i].ct-ct), min(fabs(p[i].ct+360.0-ct), fabs(p[i].ct-360.0-ct))));
                    c2 = max(c2, min(fabs(p[i].nt-nt), min(fabs(p[i].nt+360.0-nt), fabs(p[i].nt-360.0-nt))));
                }
                ans = min(ans, max(c1, c2));
            }
        }
    }
    printf("%.6lf\n", ans);
  
    return 0;
}
TLE40

它T了,考虑优化:中间有一层循环枚举n,目的是找到所有表构成的最大误差(最后找的是最大误差的最小值),但是直接枚举没有必要,最大误差其实就是把当前枚举到的指针的位置旋转180°,找旋转后指针的前驱和后继其中误差更大的那个(也就是和旋转后的角夹角最小)。

所以不需要二分,暴力枚举也可以过。(啊不过要说upper_bound是二分应该也算是吧)

注意细节:upper_bound函数的返回可能是空值,而表是一个环,所以n+1->1,0->n。

#include <bits/stdc++.h>
  
using namespace std;
  
typedef long long ll;
const int maxn = 5e4 + 3;
const int N = 1e6 + 2;
const ll mod = 1e9 + 7;

int n, h[maxn], m[maxn], s[maxn];
double hour[maxn], mint[maxn];
double ans = mod*1.0;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

double Abs(double a, double b)
{
    if(a > b) return a-b>=180?360-(a-b):a-b;
    else return b-a>=180?360-(b-a):b-a;
}

double Max(double a, double b)
{
    return a>b?a:b;
}

double angle(int h, int m, double s, bool fl)
{
    if(fl) return h*30.0+m*0.5+s*0.5/60.0;
    else return m*6.0+s*0.1;
}

double calc(int h, int m, double s, double c[], bool fl)
{
    double mx;
    double z = angle(h, m, s, fl);
    if(z <= 180) mx = 180.0+z;
    else mx = z-180.0;
    int a = upper_bound(c+1, c+1+n, mx)-c;
    int b = a - 1;
    if(a == n+1) a = 1;
    if(b == 0) b = n;
    return Max(Abs(c[a], z), Abs(c[b], z));
}

int main()
{
    freopen("unreal.in", "r", stdin);
    freopen("unreal.out", "w", stdout);
    
    n = read();
    for(int i=1; i<=n; i++)
    {
        h[i] = read()%12; m[i] = read(); s[i] = read();
        hour[i] = angle(h[i], m[i], s[i], 1);
        mint[i] = angle(h[i], m[i], s[i], 0);
    }
    sort(hour+1, hour+1+n);
    sort(mint+1, mint+1+n);
    for(int x=0; x<12; x++)
    {
        for(int y=0; y<60; y++)
        {
            for(double z=0; z<60; z+=0.01)
            {
                double t1 = calc(x, y, z, hour, 1);
                double t2 = calc(x, y, z, mint, 0);
                ans = min(ans, max(t1, t2));
            }
        }
    }
    printf("%.5lf", ans);
  
    return 0;
}
View Code %%%351238

 

D. 记忆碎片

n=5的暴力:dfs对两点之间的连边生成排列,根据每个排列建个新图,在新图上跑最小生成树,如果结果和他给的一样,ans++. 注意是边的排列,不能按点的大小开数组,WA可能是因为RE.

#include <bits/stdc++.h>
  
using namespace std;
  
typedef long long ll;
const int maxn = 40;
const int N = 1e6 + 2;
const ll mod = 1e9 + 7;

int n, tr[maxn], mx, fa[maxn], b[maxn], tree[maxn], ui;
bool v[maxn];
ll ans;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}
/*
struct node 
{
    int next, to, w;
}a[maxn<<1];
int head[maxn], len;

void add(int x, int y, int w)
{
    a[++len].to = y; a[len].next = head[x]; a[len].w = w;
    head[x] = len; 
}
*/
struct node2 
{
    int fr, to, w;
    bool operator < (const node2 &T) const 
    {
        return w < T.w;
    }
}e[maxn*maxn];
int tot;

void add2(int x, int y, int w)
{
    e[++tot].fr = x; e[tot].to = y; e[tot].w = w;
}

int find(int x)
{
    if(x == fa[x]) return x;
    return fa[x] = find(fa[x]);
}

bool check()
{
    /*for(int i=1; i<=mx; i++)
    {
        printf("%d ", b[i]);
    }
    printf("\n");*/
    
    int cnt = 0;
    //memset(head, 0, sizeof(head));
    //len = 0; 
    tot = 0;
    for(int i=1; i<n; i++)
    {
        for(int j=i+1; j<=n; j++)
        {
            //add(i, j, b[++cnt]);
            //add(j, i, b[cnt]);
            add2(i, j, b[++cnt]);
        }
    }
    sort(e+1, e+1+tot);
    for(int i=1; i<=n; i++) fa[i] = i;
    int k = 0; ui = 0;
    for(int i=1; i<=tot; i++)
    {
        int fx = find(e[i].fr), fy = find(e[i].to);
        if(fx == fy) continue;
        fa[fx] = fy;
        tree[++ui] = e[i].w;
        if(ui > n-1) break;
    }
    //if(tree[1] == 1 && tree[2] == 2 && tree[3] == 3 && tree[4] == 5) return 1;
    /*for(int i=1; i<=ui; i++)
    {
        printf("%d ", tree[i]);
    }
    printf("\n");*/
    for(int i=1; i<n; i++)
    {
        //printf("cmp : %d %d\n", tree[i], tr[i]);
        if(tree[i] != tr[i]) return 0;
    }
    return 1;
}

void dfs(int a, int n)
{
    if(a > n)
    {
        if(check()) ans = (ans + 1) % mod;
        return;
    }
    for(int i=1; i<=n; i++)
    {
        if(v[i]) continue;
        v[i] = 1;
        b[a] = i;
        dfs(a+1, n);
        v[i] = 0;
    }
}

int main()
{
    freopen("tree.in", "r", stdin);
    freopen("tree.out", "w", stdout);
    
    n = read();
    for(int i=1; i<n; i++)
    {
        tr[i] = read();
    }
    /*for(int i=1; i<n; i++)
    {
        printf("%d ", tr[i]);
    }
    printf("\n");*/
    mx = n*(n-1)/2;
    dfs(1, mx);
    printf("%lld\n", ans);

    return 0;
}
TLE10

 正解:

先来一个原版from Chen_jr

statue结构体里的bitset表示从右往左数的第几位就是连通块的大小是几,01表示这个大小的连通块有没有,rem[i]表示这个位置(从右往左数第i个)大小的连通块有多少个。

get_trans进行任意两个连通块的合并,初始化就是大小为1的连通块有n个。

预处理状态时建的转移边就是从当前状态能指向下一个状态的单向边,用它来找到能往哪儿合并。

根据循环的定义,刚加完最后一条树边就结束了,但是可能还有比最后一个树边更大的非树边,所以最后要把它们加进去,这里不需要像上面一样用到下降幂是因为可选的边和剩余位置的数量一定相等。

原版遇到不合法的数据会RE是因为sum减了一个比它大的数再取模变成了非常大的正数,但其实连通的边的数量比一定要加入的边的数量还少一定是不合法,所以这里不需要+mod)%mod,这样就可以以n<m为条件返回0了。

还有原版里sort v[]是多余的,审题啊,本来就是从小到大给的!

#include <bits/stdc++.h>
  
using namespace std;
  
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 42;
const int N = 1e6 + 2;
const ull base = 233;
const ull mod = 1e9 + 7;
const ll inv2 = 500000004;

int v[maxn], cnt, n, fac[5005], inv[5005];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

ll qpow(ll a, ll b)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

void pre()
{
    int up = 1600;
    fac[0] = 1;
    for(int i=1; i<=up; i++)
    {
        fac[i] = 1ll*fac[i-1]*i%mod;
    }
    inv[up] = qpow(fac[up], mod-2);
    for(int i=up-1; i; i--)
    {
        inv[i] = 1ll*inv[i+1]*(i+1)%mod;
    }
    inv[0] = 1;
}

struct statue
{
    bitset<44> b;
    int rem[maxn];
    void del(int x, int val)
    {
        rem[x] -= val; if(!rem[x]) b[x] = 0;
    }
    void add(int x, int val)
    {
        if(!rem[x] && val) b[x] = 1;
        rem[x] += val;
    }
    ull get_hash()
    {
        ull ans = 0;
        for(int i=1; i<=n; i++)
        {
            ans = (ans*base+rem[i])%mod;
        }
        return ans;
    }
}zt[50005];

int down(int n, int m)
{
    //if(n > 5000) return 0;
    if(n < m) return 0;
    return 1ll*fac[n]*inv[n-m]%mod;
}

struct edge 
{
    int to, net, sizex, sizey;
}e[4000005];
int head[45535], tot;

void add(int u, int v, int sx, int sy)
{
    e[++tot].net = head[u];
    head[u] = tot;
    e[tot].to = v;
    e[tot].sizex = sx;
    e[tot].sizey = sy;
}

queue<int> q;
map<ull, int> mp;

void get_tans()
{
    cnt = 1;
    zt[1].add(1, n);
    q.push(1);
    mp[zt[1].get_hash()] = 1;
    while(!q.empty())
    {
        int x = q.front(); q.pop();
        //printf("x = %d\n", x);
        statue nzt = zt[x];
        for(int now=zt[x].b._Find_first(); now<=n&&zt[x].b[now]; now=zt[x].b._Find_next(now))
        {
            if(zt[x].rem[now] > 1)
            {
                nzt.del(now, 2);
                nzt.add(now+now, 1);
                if(!mp[nzt.get_hash()])
                {
                    zt[++cnt] = nzt; q.push(cnt); mp[nzt.get_hash()] = cnt;
                }
                add(x, mp[nzt.get_hash()], now, now);
                nzt.del(now+now, 1); nzt.add(now, 2);
            }
            nzt.del(now, 1);
            for(int nxt=zt[x].b._Find_next(now); nxt<=n&&zt[x].b[nxt]; nxt=zt[x].b._Find_next(nxt))
            {
                nzt.del(nxt, 1); nzt.add(now+nxt, 1);
                if(!mp[nzt.get_hash()])
                {
                    zt[++cnt] = nzt; q.push(cnt); mp[nzt.get_hash()] = cnt;
                }
                add(x, mp[nzt.get_hash()], now, nxt);
                nzt.add(nxt, 1); nzt.del(now+nxt, 1);
            }
            nzt.add(now, 1);
        }
    }
}

int dp[43][40005];

int main()
{
    freopen("tree.in", "r", stdin);
    freopen("tree.out", "w", stdout);
    
    n = read(); pre();
    for(int i=1; i<n; i++) v[i] = read();
    //sort(v+1, v+n);
    get_tans();
    //printf("111\n");
    //printf("cnt = %d\n", cnt);
    dp[0][1] = 1;
    for(int now=1; now<n; now++)
    {
        if(now > 1)
        {
            for(int i=1; i<=cnt; i++)
            {
                if(dp[now-1][i])
                {
                    int sum = 0;
                    for(int x=zt[i].b._Find_first(); x<=n&&zt[i].b[x]; x=zt[i].b._Find_next(x))
                    {
                        sum = (sum+1ll*x*(x-1)/2*zt[i].rem[x]%mod)%mod;
                    }
                    //printf("sum = %d\n", sum);
                    //sum = (sum-v[now-1]+mod)%mod;
                    //printf("sum = %d\n", sum);
                    sum -= v[now-1];//sum一定要更大才合法,不用取模
                    dp[now-1][i] = 1ll*dp[now-1][i]*down(sum, v[now]-v[now-1]-1)%mod;
                }
            }
        }
        for(int i=1; i<=cnt; i++)
        {
            if(dp[now-1][i])
            {
                for(int j=head[i]; j; j=e[j].net)
                {
                    int v = e[j].to;
                    int sx = e[j].sizex, sy = e[j].sizey;
                    if(sx != sy) dp[now][v] = (dp[now][v]+1ll*dp[now-1][i]*zt[i].rem[sx]%mod*zt[i].rem[sy]%mod*sx%mod*sy%mod)%mod;
                    else dp[now][v] = (dp[now][v]+1ll*dp[now-1][i]*zt[i].rem[sx]%mod*(zt[i].rem[sx]-1)%mod*inv2%mod*sx%mod*sx%mod)%mod;
                }
            }
        }
    }
    int end;
    for(int i=1; i<=cnt; i++)
    {
        if(zt[i].b[n]) {end = i; break; }
    }
    //printf("end = %d\n", end);
    int res = n*(n-1)/2-v[n-1];
    int ans = 1ll*dp[n-1][end]*fac[res]%mod;
    printf("%d\n", ans);

    return 0;
}
View Code

 

posted @ 2022-08-18 21:25  Catherine_leah  阅读(40)  评论(0编辑  收藏  举报
/* */