6402. 【NOIP2019模拟11.01】Cover(启发式合并)
题目描述
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;
}