题解:AT_agc069_a [AGC069A] Schedule Optimization
好题,我的做法似乎和AT官方完全不同。
首先大致做法比较显然,直接递归处理子问题,处理完
我们先从最简单的例子考虑,现在处理的区间长度为
不妨分讨
-
如果当前的两人区间已经相交了,那么我们是不需要对其进行任何操作的,进一步说,我们需要让胜利的人的时间区间尽可能的长。
容易发现,我们新产生的区间左端点是固定的,一定为
\max(l_1,l_2) ,因为在这个瞬间,两人胜负即可决定,显然向后拖没有意义。既然左端点是确定的,那么我们就需要让右端点尽可能的长,我们如果选择
1 号,那么右端点是r_1 ,选择2 号右端点是r_2 ,为了让右端点尽可能的长,我们最后的区间实际上是(\max(l_1,l_2),\max(r_1,r_2)) 。 -
如果当前区间没有相交,即
r_1<l_2 。那么为了让二人能拼出胜负,我们需要花钱改动区间,显然当前的最优花费一定是l_2-r_1 ,即挪动两者的端点使其相交,再此基础上,我们要让拼出来的区间尽量的长,那么最优策略一定是挪动l_2 到r_1 ,如此操作,拼出来的区间为(r_1,r_2) 。
这样两人的合并就做完了,但是接下来的合并不止于此。
如果我们需要合成四个人的答案, case1 由于没有花费,不会有区别,但是 case2 就有大区别了。
考虑
在这种情况下,我们的
那么此时(即
这样的想法非常之对,即我当前的
那么如何维护
当我们遇到
代码实现也比较简单,由于数据范围小我就用了
\text{CODE}
#include<bits/stdc++.h>
#define int long long
#define N 600005
#define ls (now<<1)
#define rs (now<<1|1)
using namespace std;
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*10+ch-'0';ch=getchar();}
return x*f;
}
int n,lim,L[N*4],R[N*4];
int ans,ct[N*4];
struct node
{
int l,r;
}v[N*4];
vector<int> s[N*4];
node get(int now)
{
node a=v[ls],b=v[rs];
for(int x:s[ls])s[now].push_back(x);
for(int x:s[rs])s[now].push_back(x);
if(ct[ls])while(ct[ls]--)s[now].push_back(a.l);
if(ct[rs])while(ct[rs]--)s[now].push_back(b.l);
sort(s[now].begin(),s[now].end());
if(a.l>b.l)swap(a,b);
while(s[now].size()&&b.l==*s[now].rbegin())
{
ct[now]++;
s[now].pop_back();
}
if(b.l>a.r)
{
if(ct[now]>1)
{
ans+=(b.l-a.r);
s[now].push_back(a.r);
ct[now]--;
return {b.l,b.r};
}
if(*s[now].rbegin()<=a.r)
{
while(s[now].size()&&*s[now].rbegin()==a.r)ct[now]++,s[now].pop_back();
ans+=(b.l-a.r);
return {a.r,b.r};
}
else
{
int w=*s[now].rbegin();
while(s[now].size()&&*s[now].rbegin()==w)ct[now]++,s[now].pop_back();
ct[now]--;
s[now].push_back(a.r);
ans+=(b.l-a.r);
return {w,b.r};
}
}
return {b.l,max(a.r,b.r)};
}
void solve(int now,int l,int r)
{
if(l==r)
{
v[now]={L[l],R[l]};
ct[now]=1;
return ;
}
int mid=(l+r)>>1;
solve(ls,l,mid);
solve(rs,mid+1,r);
v[now]=get(now);
}
signed main()
{
n=read();
lim=(1<<n);
for(int i=1;i<=lim;i++)L[i]=read(),R[i]=read();
solve(1,1,lim);
cout<<ans<<"\n";
return 0;
}