题目描述
ZLOIers\text{ZLOIers}ZLOIers 喜欢 Play LOL\text{Play LOL}Play LOL。
一次放假,nnn个 ZLOIers\text{ZLOIers}ZLOIers 准备 Play\text{Play}Play。
每个 ZLOIer\text{ZLOIer}ZLOIer 的空闲时间可以用一段区间表示[ai,bi)[a_i,b_i)[ai,bi)。
现在 nnn 个人准备分成kkk组Play\text{Play}Play。
每个人恰好加入一组,每组至少一个人 。
每组能够 Play\text{Play}Play 的时间取决于每个人时间的交。
并且要求每组的人都能够一起的时间大于 000。
求最大化所有组 Play 的总时间。
输入格式
第一行包括两个整数 nnn 和 kkk。
接下来 nnn 行每行包括两个整数 aia_iai 和 bib_ibi。
输出格式
输出最大总时间,每组只算进答案一次。
如果无解,输出 000。
样例 #1
样例输入 #1
4 2
1 3
1 5
4 6
2 7
样例输出 #1
4
提示
20%20\%20% 数据,n≤15,k≤15n \leq 15, k \leq 15n≤15,k≤15;
40%40\%40% 的数据,n≤100,k≤100n\leq 100,k\leq 100n≤100,k≤100;
60%60\%60% 的数据,n≤1000,k≤1000n\leq 1000,k\leq 1000n≤1000,k≤1000;
80%80\%80% 的数据,n≤3000,k≤3000n\leq 3000,k\leq 3000n≤3000,k≤3000;
100%100\%100% 的数据,n≤8000,k≤8000,1<a<b<105n\leq 8000,k\leq 8000,1\lt a\lt b\lt 10^5n≤8000,k≤8000,1<a<b<105。
解题思路
考虑使用树形 dp 统计答案。
注意到,对于每条路径 (x,y)(x,y)(x,y),其关键节点为 lca(x,y)lca(x,y)lca(x,y)。所以我们在此处更新答案。
记 fxf_xfx 表示 xxx 为根的子树的答案,gxg_xgx 表示以 xxx 的所有儿子为根的子树的答案的和,ppp 为与该路径有交点的已经选取的路径的总数,aia_iai 和 bib_ibi 为路径 iii 的两端点,EEE 为两端点最近公共祖先为 xxx 的路径的集合,显然有转移方程 fx=max(gx,maxii∈Egx−p+1)f_x=\max(g_x,\max_{i}^{i∈E} g_x-p+1)fx=max(gx,maxii∈Egx−p+1)。
容易发现,对 ppp 的统计是时间上的瓶颈所在。此处可以记录以每个点是否被作为已选取的路径的最近公共祖先,如是则其点权为 111,则对 ppp 的计算转化为对 aia_iai 到 bib_ibi 的路径上的点权求和,然后就可以用树链剖分和线段树维护了。注意更新 fxf_xfx 时注意记录 fxf_xfx 是否、被哪条路径更新,更新完后先对路径上所有节点权值赋值为 000,然后再对 xxx 的点权 +1+1+1。时间复杂度 O(nlog2n)O(n \log^2 n)O(nlog2n)。
参考代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ls(p) p<<1
#define rs(p) p<<1|1
const int N=2e5+10;
namespace IO{
inline int read(){ int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; }
inline void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); }
}using namespace IO;
namespace code{
int n,m,head[N],tot=1,fa[N],son[N],top[N],deep[N],size[N],dfn[N],cnt,f[N],g[N];
struct node{ int x,y; };
stack<node> st[N];
struct edge{ int ver,next; }t[N<<1];
void add(int x,int y){ t[++tot].ver=y,t[tot].next=head[x],head[x]=tot; }
void dfs1(int x){
size[x]=1;
int maxson=-1;
for(int i=head[x];i;i=t[i].next){
int y=t[i].ver;
if(y==fa[x]) continue;
fa[y]=x,deep[y]=deep[x]+1,dfs1(y),size[x]+=size[y];
if(size[y]>maxson) son[x]=y,maxson=size[y];
}
}
void dfs2(int x,int fr){
top[x]=fr,dfn[x]=++cnt;
if(son[x]) dfs2(son[x],fr);
for(int i=head[x];i;i=t[i].next){
int y=t[i].ver;
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
struct Segment_Tree{
struct t_node{
int sum,lz_tag;
}t[N<<2];
void push_up(int p){
t[p].sum=t[ls(p)].sum+t[rs(p)].sum;
}
void push_down(int p,int l,int r){
if(t[p].lz_tag==-1) return;
int mid=(l+r)>>1;
t[ls(p)].lz_tag=t[rs(p)].lz_tag=t[p].lz_tag,t[ls(p)].sum=t[p].lz_tag*(l-mid+1),t[rs(p)].sum=t[p].lz_tag*(r-mid);
t[p].lz_tag=-1;
}
void build(int p,int l,int r){
t[p].lz_tag=-1;
if(l==r) return;
int mid=(l+r)>>1;
build(ls(p),l,mid),build(rs(p),mid+1,r);
}
void update(int p,int l,int r,int ul,int ur,int k){
if(ul<=l&&r<=ur) return t[p].lz_tag=k,t[p].sum=k*(r-l+1),void();
push_down(p,l,r);
int mid=(l+r)>>1;
if(ul<=mid) update(ls(p),l,mid,ul,ur,k);
if(ur>mid) update(rs(p),mid+1,r,ul,ur,k);
push_up(p);
}
void add(int p,int l,int r,int pos,int k){
if(l==r) return t[p].sum+=k,void();
push_down(p,l,r);
int mid=(l+r)>>1;
if(pos<=mid) add(ls(p),l,mid,pos,k);
else add(rs(p),mid+1,r,pos,k);
push_up(p);
}
int query(int p,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr) return t[p].sum;
push_down(p,l,r);
int mid=(l+r)>>1,ret=0;
if(ql<=mid) ret=query(ls(p),l,mid,ql,qr);
if(qr>mid) ret+=query(rs(p),mid+1,r,ql,qr);
return ret;
}
}tr;
int lca(int x,int y){
int bx=0,by=0;
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]) swap(x,y),swap(bx,by);
bx=top[x],x=fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
return x;
}
void qupd(int x,int y){
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]) swap(x,y);
tr.update(1,1,n,dfn[top[x]],dfn[x],0),x=fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
tr.update(1,1,n,dfn[x],dfn[y],0);
}
int qsum(int x,int y){
int ret=0;
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]) swap(x,y);
ret+=tr.query(1,1,n,dfn[top[x]],dfn[x]),x=fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
return ret+tr.query(1,1,n,dfn[x],dfn[y]);
}
void dp(int x){
for(int i=head[x];i;i=t[i].next){
int y=t[i].ver;
if(y==fa[x]) continue;
dp(y),g[x]+=f[y];
}
f[x]=g[x];
int nx=0,ny=0;
while(!st[x].empty()){
int y=st[x].top().x,z=st[x].top().y,k=qsum(y,z);
st[x].pop();
if(f[x]<g[x]-k+1) nx=y,ny=z,f[x]=g[x]-k+1;
}
if(nx) qupd(nx,ny),tr.add(1,1,n,dfn[x],1);
}
void solve(){
n=read(),m=read();
for(int i=1,x,y;i<n;++i) x=read(),y=read(),add(x,y),add(y,x);
dfs1(1),dfs2(1,1),tr.build(1,1,n);
for(int i=1,x,y,z;i<=m;++i) x=read(),y=read(),z=lca(x,y),st[z].push((node){x,y});
dp(1),write(f[1]);
}
}
signed main(){
freopen("paths.in","r",stdin);
freopen("paths.out","w",stdout);
code::solve();
return 0;
}
1719

被折叠的 条评论
为什么被折叠?



