[BalticOI 2021 Day2] The short shank题解
Pekemetier · · 题解
题意:
若干间牢房,其中一些会在
t_i 暴乱,每过一时刻所有暴乱的人会使右边的下一个人暴乱(如果没暴乱的话)。可以在至多D 个人右边放挡板,使其无法传染给下一个人。求T 时刻最少有多少人暴乱。
显然对于
同时显然的还有:对于一段连续的普通点,最多只会在其左端点的左侧放置隔板;对于一段连续的特殊点,最多只会在其右端点的右测放置隔板。
然后我们会想到两种统计贡献的方式:对特殊点统计和对普通点统计。
但在研究一番后,发现没有有效的对关键点统计的方法。主要是因为只能设计 dp 维护,但它的状态数是
于是我们对普通点进行统计,令
但如果这样进行 dp ,状态数还是
证明:即证不存在
l_i<l_j<i<j ,此时有l_j+(T-t_{l_j})\geq j>i ,所以l_i 应该是更大的l_j ,矛盾。
对于每个普通点,把满足
因此在树上选
证明:首先对于一个最优解,每一条链都会让最上端点尽量往上。那么对于一种可能的最优解:
图丑勿怪讨论某一点的子树,对于一条过根却不是最长链的链(A),如果最长链剩下部分有边,不妨(?)假设剩下部分只有一条链(B),且其包含了最长链剩下部分所有的点,那么将 A 下半部与 B 交换,答案不变;如果最长链剩下部分无边,将 A 直接变成最长链,答案也不降。
对于 “不妨”,可以先向下讨论最长链下方子树,就能得到最长链内只有一条链的结论。
建树时可以顺序依次枚举,将还能施加影响的特殊点及
把没有
Code:
#include<cstdio>
#include<queue>
#define max(a,b) (a>b?a:b)
using namespace std;
int n,d,t,a[2000001],ans;
int h[2000001],hp[2000001];
int st[2000001],v[2000001],num;
bool vis[2000001];
int main()
{
scanf("%d%d%d",&n,&d,&t);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
if(a[i]<=t)
{
st[++num]=i;
v[num]=i;
}
else
{
while(num&&st[num]+t-a[st[num]]<i)
{
if(h[v[num]]>h[i])
{
h[i]=h[v[num]];
hp[i]=v[num];
}
--num;
}
if(num)
{
if(h[v[num]]>h[i])
{
h[i]=h[v[num]];
hp[i]=v[num];
}
++h[i];
vis[hp[i]]=1;
v[num]=i;
}
else ++ans,vis[i]=1;
}
}
priority_queue<int>q;
for(int i=n;i;--i)
if(!vis[i])
q.push(h[i]);
while(d--)
{
if(q.empty())break;
ans+=q.top();q.pop();
}
printf("%d",n-ans);
}