BalticOI2021 The short shank(牢房)
作者:互联网
The short shank(牢房) BalticOI 2021 Day2 loj3561
首先,如果我们钦定了哪些位置放床垫,那么第i个位置上的人造反的时间就是\(\min_{j=pre}^{i}t[j]-j+i\),其中pre表示i前面里i最近的床垫。
换句话说,对于一个人i,其在时刻T之间不会造反的条件是存在一个\(j∈[b[i],i]\),并且j在j+1之间有床垫,\(b[i]\)表示离i最近的会让i造反的点,即\(t[j]-j+i<=T的j\),可以用单调栈维护凸包(比我小比我强我就无了) \(b[i]=0\)表示不会造反
通过观察可以发现,每个点到它会影响到的点的区间 要么是包含关系要么不相交,所以可以建出一棵森林,于是问题就转化成了选择一些叶子结, 使得这些叶子到根节点路径的并尽可能大,长链剖分一下,选择最长的d条就行了。
··
····
······
····
·······
···········
·················
这些区间大概长这样(懒得画图了qwq)
这里还有一个小trick,因为建出的森林,我们可以给这些森林找个root n+1( n+1不是真实存在的),这样森林就变成了一棵树,计算答案的时候再减去n+1这个不存在的1 就行了
点击查看代码
//好耶 我彻底悟了!!! 感谢几位大佬的题解
//刚才去学习了一下长链剖分(之前因为不会长链剖分就一直没补) 就是把一棵树的结点用几条不相交的链串起来;
//和轻重链剖分很像 轻重链剖分的重儿子是子节点个数最多的儿子 长链剖分的重儿子是子节点深度最深的儿子
//#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define _ 0
const int maxn=2e6+5;
const int inf=0x3f3f3f3f;
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;
}
int a[maxn],b[maxn];
//bi表示离i最近的会让i造反的点 可以用单调栈维护凸包(比我小比我强我就无了)
//bi=0表示不会造反
//因为每个点到它会影响到的点的区间 要么是包含关系要么不相交 所以可以建出一棵树
//于是问题就转化成了选择一些叶子结点 使得这些叶子到根节点路径的并尽可能大
//长链剖分一下 选择最长的d条就行了
struct node{
int to,nxt;
}edge[maxn];
int head[maxn],tot;
void addedge(int u,int v)
{
edge[++tot].to=v;
edge[tot].nxt=head[u];
head[u]=tot;
}
int stk[maxn],stop=0;
int dep[maxn],son[maxn],top[maxn];
vector<int> v;
void dfs1(int x)
{
dep[x]=1;
for(int i=head[x];i;i=edge[i].nxt)
{
int y=edge[i].to;
dfs1(y);
if(dep[x]<dep[y]+1)
{
dep[x]=dep[y]+1;
son[x]=y;
}
}
}
void dfs2(int x,int tp)
{
top[x]=tp;
if(son[x])
dfs2(son[x],tp);
if(top[x]==x)
v.push_back(dep[x]);
for(int i=head[x];i;i=edge[i].nxt)
{
int y=edge[i].to;
if(y!=son[x])
dfs2(y,y);
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
//freopen("prisoner.in","r",stdin);
//freopen("prisoner.out","w",stdout);
int n,d,t;
cin>>n>>d>>t;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
{
while(stop&&a[stk[stop]]-stk[stop]>=a[i]-i)
stop--;
stk[++stop]=i;
while(stop&&a[stk[stop]]-stk[stop]>t-i)
stop--;
b[i]=stk[stop];
}
stk[0]=n+1;stop=0;
int num=0;
for(int i=n;i>=1;i--)
{
if(b[i]==i)
continue;
if(b[i]==0)
{
num++;
continue;
}
while(stop&&b[stk[stop]]>b[i])
stop--;
addedge(stk[stop],i);
stk[++stop]=i;
}
dfs1(n+1);
dfs2(n+1,n+1);
sort(v.begin(),v.end(),greater<int>());
for(int i=0;i<min((int)v.size(),d);i++)
num+=v[i];
cout<<n-num+1<<endl; //这里+1是因为能救的人要少一个(n+1) 加边的时候 n+1是root 但n+1并不存在 所以少救一个
return ~~(0^_^0);
}
/*
Notes:
1.看所有题目
2.注意数据范围
3.想想自己还能做什么而不是做了什么
4.看清题目!!!
5.记得把调试代码删掉!!!!
6.longlong时 1要写成1ll
7.Think twice code once, think once code forever!
*/
标签:长链,short,shank,int,stop,stk,--,BalticOI2021,ch 来源: https://www.cnblogs.com/zxi8-may/p/16302054.html