首页 > 其他 > 详细

6402. 【NOIP2019模拟11.01】Cover(启发式合并)

时间:2019-11-07 20:30:25      阅读:138      评论:0      收藏:0      [点我收藏+]

题目描述

Description

小 A 现在想用 ?? 条彩灯去装饰家中的走廊,走廊可以视作一个 [1, ??] 的区间,每一条彩灯都能覆盖一个子区间,并且有一个特定的美观程度。
然而为了降低装饰的难度,彩灯能够覆盖的区间两两之间只有包含和不相交的关系,同时为了避免光污染,他希望每个 [1, ??] 中的点至多被 ?? 条彩灯覆盖。
现在小 A 希望你能告诉他,?? = 1, 2, … , ?? 时,选出的彩灯的最大美观程度之和时多少。

Input

第一行两个个整数 ??, ?? 表示区间的长度与彩灯的数量。
接下来 ?? 行,每行三个整数 ???? , ???? , ???? 表示一条彩灯能够覆盖的区间以及它的美观程度。

Output

输出一行 ?? 个整数,第 ?? 个数表示 ?? = ?? 时的最大美观程度。

Sample Input

25 6
1 2 10
2 3 10
1 3 21
3 4 10
4 5 10
3 5 19 

Sample Output

41 80 80 80 80 80

Data Constraint

对于 25% 的数据,?? ≤ 20
对于 45% 的数据,??, ?? ≤ 5000
对于另外 25% 的数据,所有 ???? 相同
对于 100% 的数据,1 ≤ ???? ≤ ???? ≤ ??, ?? ≤ 300000, ???? ≤ 10^9

题解

题目中给出的其实是端点,覆盖的是两点中间的区间

用栈处理出区间之间的关系,问题变成给出一棵树,每个点有权值,选若干节点使得任意一点到根的选取节点个数<=k(k=1~m)的最大权值和

n^2dp显然,设f[i][j]表示以i为根的子树中选取节点个数<=j的最大权值和
f[i][j]=min(∑f[son][j],∑f[son][j-1]+a[i])

可以发现f[i][j+1]-f[i][j]的值单调不增

感性证明:

考虑由j的选择方案变为j+1的方案,可以分为删点和加点两步

删点的目的是为了选择其它的某些点,所以其实是在j不变的情况调整点的选择,之后再加新点

若调整的收益>0,那么必然在j的时候就已经调整完了,若<=0则没有必要

如果只考虑加点的情况下增量变大,那么可以把j-1-->j和j-->j+1的新增点交换一下

于是可以set启发式合并维护差分表

具体写法:轻重链剖分,先走重儿子(不用新建set,相当于直接上传),走完后走轻儿子(新建set,走完后合并到重儿子上面)

这样只需要开log n个set,时间为O(n log^2 n)

合并儿子就对应位置相加,+a[i]就把a[i]丢到set里

证明:
设f[i][j+1]-f[i][j]<a[i],设f[i][j]=x,设f[i][j]-->f[i][j+1],f[i][j+1]-->f[i][j+2]...的差值为b[1],b[2]...,那么a[i]>b[1]>=b[2]...

那么原先的序列为x,x+b[1],x+b[1]+b[2]...

dp更新之后为x,x+a[i],x+a[i]+b[1]...

差值为a[i],b[1],b[2]...

可以发现,实际上相当于把后面的差值后移了一位,所以直接把a[i]丢到set里就行了

code

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <set>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define max(a,b) (a>b?a:b)
using namespace std;

struct type{
    int l,r,s;
} A[300001];
int a[300001][2];
int B[16];
int ls[300001];
int d[300001];
int d2[300001]; //father(bfs)/tier(dfs)
int fa[300001];
long long ans[300001];
bool bz[300001];
int size[300001];
int nx[300001];
int mx[300001];
long long b[300001];
int Ls[300001];
bool Bz[300001];
char st[10800001];
char *Ch=st;
char st2[4800001];
multiset<long long,greater<long long> > S[21];
multiset<long long,greater<long long> > :: iterator I;
int n,m,i,j,k,l,len,h,t,Len;

bool cmp(type a,type b)
{
    return a.r<b.r || a.r==b.r && a.l>b.l;
}

void New(int x,int y)
{
    ++len;
    a[len][0]=y;
    a[len][1]=ls[x];
    ls[x]=len;Ls[x]=len;
}

int get()
{
    int x=0;
    
    while (*Ch<'0' || *Ch>'9') *++Ch;
    while (*Ch>='0' && *Ch<='9') x=x*10+(*Ch-'0'),*++Ch;
    
    return x;
}
void put(long long x)
{
    int len=0;
    
    while (x)
    {
        B[++len]=x%10;
        x/=10;
    }
    while (len)
    st[++Len]=B[len--]+'0';
    st[++Len]=' ';
}

void bfs(int T)
{
    int i;
    
    h=0;t=1;
    d[1]=T;
    size[T]=1;
    
    while (h<t)
    {
        for (i=ls[d[++h]]; i; i=a[i][1])
        {
            ++t;
            d[t]=a[i][0];
            d2[t]=d[h];
            size[a[i][0]]=1;
        }
    }
    
    fd(i,t,2)
    {
        size[d2[i]]+=size[d[i]];
        
        if (mx[d2[i]]<size[d[i]])
        mx[d2[i]]=size[d[i]],nx[d2[i]]=d[i];
    }
    fo(i,1,t)
    if (!nx[d[i]])
    nx[d[i]]=-1,Bz[d[i]]=1;
}

void dfs()
{
    int i,j,T;
    
    while (t)
    {
        T=d[t];
        
        if (!Bz[T])
        {
            Bz[T]=1;
            
            ++t;
            d[t]=nx[T];
            d2[t]=d2[t-1];
        }
        else
        {
            if (a[Ls[T]][0]==nx[T])
            Ls[T]=a[Ls[T]][1];
            
            if (Ls[T])
            {
                ++t;
                d[t]=a[Ls[T]][0];
                d2[t]=d2[t-1]+1;
                
                Ls[T]=a[Ls[T]][1];
            }
            else
            {
                if (t>1 && d2[t-1]!=d2[t])
                {
                    S[d2[t]].insert(A[d[t]].s);
                    
                    j=0;
                    while (!S[d2[t]].empty())
                    {
                        ++j;
                        I=S[d2[t]].begin();
                        b[j]=*I;
                        S[d2[t]].erase(I);
                        
                        I=S[d2[t]-1].begin();
                        b[j]+=*I;
                        S[d2[t]-1].erase(I);
                    }
                    while (j)
                    S[d2[t]-1].insert(b[j--]);
                }
                else
                S[d2[t]].insert(A[d[t]].s);
                
                --t;
            }
        }
    }
}

int main()
{
    freopen("cover.in","r",stdin);
    freopen("cover.out","w",stdout);
    
    fread(st,1,10800001,stdin);
    
    n=get();m=get();
    fo(i,1,m)
    A[i].l=get(),A[i].r=get(),A[i].s=get(),--A[i].r;
    
    sort(A+1,A+m+1,cmp);
    
    l=0;
    fo(i,1,m)
    {
        while (l && A[i].l<=A[d[l]].l)
        fa[d[l--]]=i;
        d[++l]=i;
    }
    
    fo(i,1,m)
    if (fa[i])
    New(fa[i],i),bz[fa[i]]=1;
    
    fo(i,1,m)
    if (!fa[i])
    {
        bfs(i);
        
        t=1;
        d[1]=i;
        d2[1]=1;
        dfs();
        
        j=0;
        while (!S[1].empty())
        {
            I=S[1].begin();
            ans[++j]+=*I;
            
            S[1].erase(I);
        }
    }
    
    Len=-1;
    fo(i,1,m)
    {
        ans[i]+=ans[i-1];
        put(ans[i]);
    }
    st[++Len]='\n';
    
    fwrite(st,1,Len,stdout);
    
    fclose(stdin);
    fclose(stdout);
    
    return 0;
}

6402. 【NOIP2019模拟11.01】Cover(启发式合并)

原文:https://www.cnblogs.com/gmh77/p/11815190.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!