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:并查集