NOI 模拟赛

T1 binary

求有多少不同构的二叉树最长链为 $n$,答案对 $10^k$ 取模

$n \leq 200000,k \leq 64$

sol:

首先转化一下,原题要求的链长是边数,转换成点数好做一点,把 n+1 即可

记 $f_{(i,0)}$ 表示最大深度为 $i$,当前没有长为 $n$ 的链,有多少不同构二叉树

 $f_{(i,1)}$ 表示最大深度为 $i$,当前有长为 $n$ 的链,有多少不同构二叉树

因为只有二叉,考虑将两个二叉树的根向上连出一个新点,转移答案

枚举这个新的二叉树深度,显然原来两个二叉树至少有一个深度为 $i-1$

如果新的二叉树有长为 $n$ 的链,分两种情况

1.原来有一个二叉树有长为 $n$ 的链,不妨设在左边,这样只要保证右边跟左边拼出来的链长度不超过 $n$ 就可以了,显然右边深度不超过 $min(n-i,i-1)$,因为左右对称,要乘 2 ,之后因为乘了 2 ,要减去左右两边深度一样的情况,而这种情况在 $n-i<i-1$ 的时候是不存在的

2.两边都没有长为 $n$ 的链,那就一定要拼出来一条长度为 $n$ 的链,左边是 $i-1$ ,右边就是 $n-i$ ,注意 $i-1$ 要大于等于 $n-i$,等于的时候只有一种情况,不等于可以交换左右儿子

没有的话,保证拼不出来即可,也就是如果左边是 $i-1$ ,右边的上界就是 $min(n-i-1,i-1)$ ,还是可以交换左右,注意当 $i-1 \leq n-i-1$ 的时候要排除两边一样的情况

前缀和优化 dp 即可,然后因为 k 是 64 ,需要高精度

但我不想写

#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline LL read()
{
    LL x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int maxn = 200010;
LL n,k,mod = 1;
LL f[maxn][2],sum[maxn][2];
namespace subtask_low
{
    int main()
    {
        for(int i=1;i<=k;i++)mod *= 10;
        f[0][0] = 1;
        sum[0][0] = 1;
        for(LL i=1;i<=n;i++)
        {
            if(i != n)f[i][0] = (f[i][0] + (2 * f[i - 1][0] * sum[min(n - i - 1,i - 1)][0]) % mod) % mod;
            if((i - 1) < (n - i))f[i][0] = (f[i][0] + mod - ((f[i - 1][0] * f[i - 1][0]) % mod)) % mod;
            
            f[i][1] = (f[i][1] + (2 * (f[i - 1][1] * ((sum[min(n-i,i-1)][0] + sum[min(n-i,i-1)][1]) % mod)) % mod) % mod) % mod;
            if((i - 1) <= (n - i))f[i][1] = (f[i][1] + mod - (f[i - 1][1] * f[i - 1][1] % mod)) % mod;
            if((i - 1) >= (n - i))f[i][1] = (f[i][1] + (2 * f[i - 1][0] * f[n - i][0]) % mod) % mod;
            if((i - 1) == (n - i))f[i][1] = (f[i][1] + mod - ((f[i - 1][0] * f[n - i][0]) % mod)) % mod;
            
            sum[i][0] = (sum[i - 1][0] + f[i][0]) % mod;
            sum[i][1] = (sum[i - 1][1] + f[i][1]) % mod;
            //cout << f[i][0] << " " << f[i][1] << endl;
        }
        cout << sum[n][1] << endl;
        return 0;
    }
}
int main()
{
    freopen("binary.in","r",stdin);
    freopen("binary.out","w",stdout);
    n = read() + 1,k = read();
    subtask_low::main();
}
T1

 

T2 sort

求一个有向图字典序最小的拓扑序和字典序最大的拓扑序

$n \leq 200000,m=n$

sol:

送分题,字典序最大的拓扑序直接用个堆,字典序最小的就建反图用个堆

#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int maxn = 200010;
int n,a[maxn];
priority_queue<int> q;
int first[maxn],to[maxn << 1],nx[maxn << 1],cnt,ind[maxn],ans[maxn],pnt,reh[maxn];
inline void add(int u,int v){to[++cnt] = v;nx[cnt] = first[u];first[u] = cnt;}
int main()
{
    freopen("sort.in","r",stdin);
    freopen("sort.out","w",stdout);
    n = read();
    for(int i=1;i<=n;i++)a[i] = read();
    
    memset(ans,0,sizeof(ans));memset(reh,0,sizeof(reh));pnt = 0;
    
    for(int i=1;i<=n;i++)if(a[i]){add(i,a[i]);ind[a[i]]++;}
    for(int i=1;i<=n;i++)if(ind[i] == 0)q.push(i);
    while(!q.empty())
    {
        int now = q.top();q.pop();
        //cout << now << " ";
        ans[++pnt] = now;
        for(int i=first[now];i;i=nx[i]){ind[to[i]]--;if(ind[to[i]] == 0)q.push(to[i]);}
    }
    for(int i=1;i<=n;i++)reh[ans[n - i + 1]] = i;
    for(int i=1;i<=n;i++)cout << reh[i] << " ";
    cout << endl;
    memset(first,0,sizeof(first));cnt = 0;memset(ind,0,sizeof(ind));
    memset(ans,0,sizeof(ans));memset(reh,0,sizeof(reh));pnt = 0;
    
    for(int i=1;i<=n;i++)if(a[i]){add(a[i],i);ind[i]++;}
    for(int i=1;i<=n;i++)if(ind[i] == 0)q.push(i);
    while(!q.empty())
    {
        int now = q.top();q.pop();
    //    cout << now << " ";
        ans[++pnt] = now;
        for(int i=first[now];i;i=nx[i])
        {
            ind[to[i]]--;
            if(ind[to[i]] == 0)q.push(to[i]);
        }
    }
    for(int i=1;i<=n;i++)reh[ans[i]] = i;
    for(int i=1;i<=n;i++)cout << reh[i] << " ";
    cout << endl;
    //for(int i=pnt;i;i--)cout << ans[i] << " ";
    //cout << endl;
    memset(first,0,sizeof(first));cnt = 0;memset(ind,0,sizeof(ind));
}
T2

 

T3 mst

$q$ 次询问图中删掉一条边的最小生成树边权和,或者输出 "Not Connected"

$n,m,q \leq 100000$

sol:

每条树边记录一下能覆盖它的非树边的最小权值

用树剖套线段树区间修改单点查询

#include<bits/stdc++.h>
#define LL long long
#define maxn 250010
#define lson l, mid, x << 1
#define rson mid + 1, r, x << 1 | 1
using namespace std;
inline int read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
    return x * f;
}
struct data
{
    int x, y, z, id;
    bool vis;
} a[maxn << 1];
int f[maxn], head[maxn], to[maxn << 1], val[maxn << 1], nx[maxn << 1], cnt, reh[maxn << 1], fa[maxn], deep[maxn], si[maxn], bl[maxn],
    pos[maxn], tot, mn[maxn << 2], n;
bool cmp1(const data &a, const data &b) { return a.z < b.z; }
bool cmp2(const data &a, const data &b) { return a.id < b.id; }
int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); }
void add(int x, int y, int z) { to[++cnt] = y, val[cnt] = z, nx[cnt] = head[x], head[x] = cnt; }
void dfs1(int x)
{
    int i;
    si[x] = 1;
    for (i = head[x]; i; i = nx[i])
        if (to[i] != fa[x])
            fa[to[i]] = x, deep[to[i]] = deep[x] + 1, reh[val[i]] = to[i], dfs1(to[i]), si[x] += si[to[i]];
}
void dfs2(int x, int c)
{
    int i, k = 0;
    bl[x] = c, pos[x] = ++tot;
    for (i = head[x]; i; i = nx[i])
        if (to[i] != fa[x] && si[to[i]] > si[k])
            k = to[i];
    if (k)
    {
        dfs2(k, c);
        for (i = head[x]; i; i = nx[i])
            if (to[i] != fa[x] && to[i] != k)
                dfs2(to[i], to[i]);
    }
}
void update(int b, int e, int a, int l, int r, int x)
{
    if (b <= l && r <= e)
    {
        mn[x] = min(mn[x], a);
        return;
    }
    int mid = (l + r) >> 1;
    if (b <= mid)
        update(b, e, a, lson);
    if (e > mid)
        update(b, e, a, rson);
}
int query(int p, int l, int r, int x)
{
    if (l == r)
        return mn[x];
    int mid = (l + r) >> 1;
    if (p <= mid)
        return min(mn[x], query(p, lson));
    else
        return min(mn[x], query(p, rson));
}
void modify(int x, int y, int v)
{
    while (bl[x] != bl[y])
    {
        if (deep[bl[x]] < deep[bl[y]])
            swap(x, y);
        update(pos[bl[x]], pos[x], v, 1, n, 1), x = fa[bl[x]];
    }
    if (deep[x] > deep[y])
        swap(x, y);
    if (x != y)
        update(pos[x] + 1, pos[y], v, 1, n, 1);
}
signed main()
{
    freopen("mst.in","r",stdin);
    freopen("mst.out","w",stdout);
    int m, q, x, v, c = 0;
    LL sum = 0;
    n = read(),m = read();
    for (int i = 1; i <= m; i++) a[i].x = read(), a[i].y = read(), a[i].z = read(), a[i].id = i;
    sort(a + 1, a + m + 1, cmp1);
    for (int i = 1; i <= n; i++) f[i] = i;
    for (int i = 1; i <= m; i++)
        if (find(a[i].x) != find(a[i].y))
            sum += a[i].z, a[i].vis = 1, add(a[i].x, a[i].y, a[i].id), add(a[i].y, a[i].x, a[i].id),
                           f[f[a[i].x]] = f[a[i].y], c++;
    if (c < n - 1)
    {
        q = read();
        while (q--) puts("Not connected");
        return 0;
    }
    dfs1(1), dfs2(1, 1);
    memset(mn, 127, sizeof(mn));
    sort(a + 1, a + m + 1, cmp2);
    for (int i = 1; i <= m; i++)
        if (!a[i].vis)
            modify(a[i].x, a[i].y, a[i].z);
    q = read();
    while (q--)
    {
        x = read();
        if (!a[x].vis)
            printf("%lld\n", sum);
        else
        {
            v = query(pos[reh[x]], 1, n, 1);
            if (v == 2139062143)
                puts("Not connected");
            else
                printf("%lld\n", sum - a[x].z + v);
        }
    }
    return 0;
}
T3

 

posted @ 2019-01-26 16:38  探险家Mr.H  阅读(192)  评论(0编辑  收藏  举报