2017多校第8场 HDU 6133 Army Formations 线段树合并

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6133

题意:给你一棵n个节点的二叉树,每个节点有一个提交任务的时间,每个节点总的提交任务的罚时为:提交这个节点和其子树所有的任务,每个任务提交时间的总和为该点的罚时。求每个节点提交完所有任务的最小罚时。

解法:根据题意,我们可以知道每个节点的提交的最小罚时为,按照任务的提交时间从小到大的来提交任务,可以得到最小的罚时。所以我们可以用线段树合并,先建立权值线段树,记录权值区间L到R的所有权值sum与size,线段树上的每一个点的ans为ans[lchild]+ans[rchild]+size[rchild]*sum[lchild]。

现场队友写了Splay合并过了,我下来写线段树合并一直MLE,然后交g++卡过了。。。

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxn = 100002;
typedef long long LL;
int n,m,cnt,rt[maxn],a[maxn],b[maxn];
LL ans[maxn];
struct edge{
    int v,next;
}E[maxn*2];
int head[maxn], edgecnt;
void init(){
    memset(head,-1,sizeof(head));
    edgecnt=0;
}
void add(int u, int v){
    E[edgecnt].v=v,E[edgecnt].next=head[u],head[u]=edgecnt++;
}
struct node{
    int ls,rs,sz;
    LL ans,sum;
}T[maxn*18];
int Merge(int u, int v){
    if(!u||!v) return u+v;
    if(T[u].ls||T[u].rs){
        T[u].ls=Merge(T[u].ls,T[v].ls);
        T[u].rs=Merge(T[u].rs,T[v].rs);
        T[u].sum=T[T[u].ls].sum+T[T[u].rs].sum;
        T[u].sz=T[T[u].ls].sz+T[T[u].rs].sz;
        T[u].ans=T[T[u].ls].ans+T[T[u].rs].ans+T[T[u].ls].sum*T[T[u].rs].sz;
    }
    else{
        T[u].ans=T[u].ans+T[v].ans+T[u].sum*T[v].sz;
        T[u].sum=T[u].sum+T[v].sum;
        T[u].sz=T[u].sz+T[v].sz;
    }
    return u;
}
void dfs(int u, int fa){
    for(int i = head[u]; ~i; i=E[i].next){
        int v = E[i].v;
        if(v!=fa){
            dfs(v,u);
            Merge(rt[u],rt[v]);
        }
    }
    ans[u]=T[rt[u]].ans;
}
void build(int &node, int l, int r, int pos)
{
    node = ++cnt;
    T[node].sum=T[node].ans=b[pos];
    T[node].sz=1;
    T[node].ls=T[node].rs=0;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(pos<=mid) build(T[node].ls, l, mid, pos);
    else build(T[node].rs, mid+1, r, pos);
}
int main()
{
    int _;
    scanf("%d", &_);
    while(_--){
        cnt=0;
        scanf("%d", &n);
        init();
        for(int i=0; i<maxn; i++) T[i].sz=T[i].ans=T[i].sum=0;
        for(int i=1; i<=n; i++) scanf("%d", &a[i]), b[i]=a[i];
        sort(b+1,b+n+1);
        m = unique(b+1,b+n+1)-b-1;
        for(int i=1; i<=n; i++) a[i] = lower_bound(b+1,b+m+1,a[i])-b;
        for(int i=1; i<=n; i++) build(rt[i],1,m,a[i]);
        for(int i=1; i<n; i++){
            int u, v;
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        dfs(1,-1);
        for(int i=1; i<=n; i++){
            printf("%lld", ans[i]);
            if(i!=n) printf(" ");
            else printf(" \n");
        }
    }
    return 0;
}
时间: 2024-10-07 03:51:49

2017多校第8场 HDU 6133 Army Formations 线段树合并的相关文章

多校第4场 HDU 4902 Nice boat 线段树

思路:这题比赛的时候宝哥说的思路我觉得对的,就是当是2操作的时候,先把数放到数组里,最后查询输出的时候再统一计算,不过那时敲得烂死了,debug了两天,靠-- 上午写的vector在pushDown的时候又忘了clear了,然后MLE了一早上,尼玛,还以为用的数组太大超了,然后又改成结构体,还是MLE,最后把别人的代码交上去发现没MLE,疯了一中午,最后无聊的时候才发现这个错误,尼玛--发现自己调试怎么变得这么弱了呢-- 还有一个需要注意的问题是1与2操作的处理上比较容易出错,这也是我WA了一下

第四场 hdu 6070 Dirt Ratio (线段树+二分)

http://acm.hdu.edu.cn/showproblem.php?pid=6070 题目大意:给出的序列上的数代表颜色,求子序列中不同数字的个数X与子序列长度Y中,X/Y的最小值 解题思路:思路和官方给的想法一样 值得注意的是线段树的节点中储存的是 size(l,r)+mid×l ,在建树时 mid×l 作为树节点的初始值,然后不断更新当前颜色对于 前一个相同颜色的位置+1 到 当前位置 的节点值+1,然后询问 1 到 当前位置的最小值 是否小于mid*(i+1). 虽然最后要打印小数

HDU-DuoXiao第二场hdu 6315 Naive Operations 线段树

hdu 6315 题意:对于一个数列a,初始为0,每个a[ i ]对应一个b[i],只有在这个数字上加了b[i]次后,a[i]才会+1. 有q次操作,一种是个区间加1,一种是查询a的区间和. 思路:线段树,一开始没用lazy,TLE了,然后开始想lazy的标记,这棵线段树的特点是,每个节点维护 :一个区间某个a 要增加1所需个数的最小值,一个区间已加上的mx的最大值标记,还有就是区间和sum. (自己一开始没有想到mx标记,一度想把lazy传回去. (思路差一点就多开节点标记. #include

2017多校第8场 HDU 6143 Killer Names 容斥,组合计数

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6143 题意:m种颜色需要为两段长度为n的格子染色,且这两段之间不能出现相同的颜色,问总共有多少种情况. 解法:枚举要为这两段分配的颜色数目分别为 i,j ,则在第一段总共有 C(m,i) 种选取方案,在第二段总共有 C(m?i,j) 种选取方案.而在每段内部,我们设 F(n,x) 为长度为 n 的格子使用 x 种颜色(等于 x )染色的方案数.则根据容斥原理 F(n,x)=x^n?C(x,1)*(x

2017多校第4场 HDU 6078 Wavel Sequence DP

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6078 题意:求两个序列的公共波形子序列的个数. 解法: 类似于最长公共上升子序列,对于每个i,只考虑存在j使得a[i]==b[j]的情况. dp[i][j][0]表示以a[i]和b[j]为公共序列结尾且为波谷的情况总和. dp[i][j][1]则表示波峰的情况总和. S[i][j][0]表示sum(dp[k][j][0] | 1<=k<=j-1). S[i][j][1]则表示sum(dp[k][j

2017多校第7场 HDU 6129 Just do it 找规律

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6129 题意:求a序列后m次xor前缀和 解法: 手动对1位置对每个位置的贡献打表发现 第一次 贡献为 1 1 1 1 1 1 1 1 1 1 1 第二次 贡献为 1 0 1 0 1 0 1 0 1 0 1 0 第四次 贡献为 1 3个0 1 3个0 1 3个0 1 3个0 第八次 贡献为 1 7个0 1 7个0 1 7个0 1 7个0 ... 这是比赛之后才知道的,看着比赛的时候通过了200+人,被

2017多校第10场 HDU 6180 Schedule 贪心,multiset

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6180 题意:给了一些任务的开始时间和终止时间,现在让我们安排k台及机器,让这些任务在k太机器上最小,并且使得机器的运行时间的和最小. 解法:按开始工作的时间从小到大排序后,用一个set容器维护一下,每次加入找set里面结束时间小于等于开始时间并且最近的点插入即可,然后如果没有小于开始时间的就重新开一台机器即可,这里可能有重复元素,需要multiset. #include <bits/stdc++.h

2017多校第9场 HDU 6170 Two strings DP

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6170 题意:给了2个字符串,其中第2个字符串包含.和*两种特别字符,问第二个字符串能否和第一个匹配. 解法:dp[i][j]代表在第一个串的i位置,第2个串的j位置是否可以匹配,然后按照'*'这个特殊情况讨论转移即可. #include <bits/stdc++.h> using namespace std; const int maxn = 3005; bool dp[maxn][maxn];

2017多校第10场 HDU 6181 Two Paths 次短路

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6181 题意:给一个图,求出次短路. 解法:我之前的模板不能解决这种图,就是最短路和次短路相等的情况,证明这题数据还是水了.下来我修改了一下次短路的,就可以避免这种情况了.提供一个sample 4 4 (1,2,1)( 1, 3,1) (2 4,1) (3 ,4,1).这组的正确答案是2,算法就来看代码吧. #include <bits/stdc++.h> using namespace std;