2019-9-14题目

2019-9-14题目

accoding contest403

https://accoding.cn/contest-ng/index.html#/403

E

Q:n个节点的不同形态二叉树个数

A:

考虑固定根节点,则左子树包含节点个数为0~n-1

(b_n=sum_{i=0}^{n-1}b_i*b_{n-1-i})

即为Catalan卡特兰数,(b_n=frac{C(n,2n)}{n+1})

[LeetCode] 96. Unique Binary Search Trees 独一无二的二叉搜索树

注意到1~n构成二叉搜索树BST的个数同样是卡特兰数,这意味着每一种二叉树的构象都唯一对应着一棵二叉搜索树(节点元素为1~n)

B

A:排序+小顶堆(优先队列)

V值降序排列,然后向小顶堆逐个插入L,每次插入L后计算当前sum

维护一个k个节点的小顶堆,只有当前影片的L大于堆顶元素时才执行真正的插入操作。但可以证明,每次都插入当前影片后调整小顶堆再pop堆顶,不影响结果的正确性。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>

struct mv{
    int l;
    int v;
};

bool cmp(mv a,mv b){
    return a.v>b.v;
}

mv s[100006];

int main(){
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++){
        scanf("%d%d",&s[i].l,&s[i].v);
    }
    std::sort(s,s+n,cmp);
    std::priority_queue<int, std::vector<int>,std::greater<int> >q;//小顶堆
    long long sum=0,ans=0;
    //下面维护一个k节点的小顶堆
    for(int i=0;i<n;i++){
        /*if(i<k){
            sum+=s[i].l;
            q.push(s[i].l);
            ans=std::max(sum*s[i].v,ans);
        }
        else{
            if(s[i].l>q.top()){
                sum+=s[i].l;
                q.push(s[i].l);
                sum-=q.top();
                q.pop();
                ans=std::max(sum*s[i].v,ans);
            }
        }*/
        sum+=s[i].l;
        q.push(s[i].l);
        if(i>=k){
            sum-=q.top();//减去堆顶
            q.pop();
        }
        ans = std::max(sum*s[i].v,ans);
        /*
        考虑新插入小顶堆的元素又被pop出的情况,这时sum依然是正确的 等于 小顶堆中所有节点之和,但s[i].v并不是这个小顶堆对应的V值,而是偏小。即这种情况会使得sum*s[i].v相比于插入之前减小。故不影响结果的正确性。
        */
    }
    printf("%lld
",ans);
}

J

ref:https://www.cnblogs.com/tian-luo/p/9674289.html

考虑完全图的MST,去掉任意一条边x之后,所得的是两个连通分量,注意到MST唯一的条件,可以知道所有连接这两个连通分量的边中,边x的权值最小且唯一,其他的边权值都必须大于x。

所以对最小生成数中的边按升序排列,依次维护加边操作和对应连通块中点的个数就行了。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

int n;//完全图的点数
long long sum=0;

struct Edge{
    int x;
    int y;
    int z;
}e[100006];

bool cmp(Edge a,Edge b){
    return a.z<b.z;
}

long long num[100006];//每个集合对应的顶点数
int p[100006];//并查集

//并查集的查询操作
int find(int x){
    return p[x]==x?x:p[x]=find(p[x]);
}

void init(){
    for(int i=1;i<=n;++i){
        p[i]=i;
        num[i]=1;
    }
    sum=0;
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        init();
        for(int i=1;i<=n-1;++i){
            scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].z);
        }
        sort(e+1,e+n,cmp);
        for(int i=1;i<=n-1;++i){
            int u=find(e[i].x);
            int v=find(e[i].y);
            sum+=(e[i].z+1)*(num[u]*num[v]-1);//坑:这里的乘法操作会溢出int,需要将num[]数组类型设为long long
            sum+=e[i].z;
            p[u]=v;
            num[v]+=num[u];
        }
        printf("%lld
",sum);
    }
}
int ~ 2e9 10位数
long long ~ 9e18 19位数
unsigned int ~ 4e9 10位数
unsigned long long ~ 1e19 20位数

D

Q:并查集

原文地址:https://www.cnblogs.com/cbw052/p/11524223.html