Luogu P4425 转盘 题解 [ 黑 ] [ 线段树 ] [ 贪心 ] [ 递归 ]
转盘:蒟蒻的第一道黑,这题是贪心和线段树递归合并的综合题。
贪心
破环成链的 trick 自然不用多说。
首先观察题目,很容易发现一个性质:只走一圈的方案一定最优。这个很容易证,因为再绕一圈回来标记前面的和等前面的标记完之后继续走是等价的,并且再绕一圈甚至可能更劣。
于是,我们只用走一圈,并且在走路的途中走走停停,就可以走出最优解。
但这依然不怎么好求,通过观察发现:我们把所有停下的时间移到前面来,在起点就把要停的时间全部停掉,和走走停停是等价的,并且这样更好求。
接下来我们就来推式子了:
假设当前的时间是
移项得:
因此在此时
由于每次走路还要计算时间,所以总时间为
因为要最小化,所以答案就是
线段树与递归
根据上面的分析,我们需要维护
首先
但是外面的那个
设
接下来考虑如何递归合并信息:
注意:当前我们处在的节点是
当递归到叶子节点时
由式子可知,直接返回
当 时
当 时
注意
其余部分就是按照线段树常规的配置来写了。
代码
#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef pair<int,int> pi;
const int N=200005;
int n,m,q,a[N],lastans=0;
struct node{
int l,r;
int mx,mi;
}tr[4*N];
int dfs(int p,int mxr)
{
if(tr[p].l==tr[p].r)return (tr[p].l+max(mxr,tr[p].mx));
int mid=(tr[p].l+tr[p].r)>>1;
if(tr[rc].mx<mxr)return min(dfs(lc,mxr),mid+1+mxr);
return min(tr[p].mi,dfs(rc,mxr));
}
void pushup(int p)
{
tr[p].mx=max(tr[lc].mx,tr[rc].mx);
tr[p].mi=dfs(lc,tr[rc].mx);
}
void build(int p,int ln,int rn)
{
tr[p]={ln,rn,a[ln]-ln,ln+a[ln]-ln};
if(ln==rn)return;
int mid=(ln+rn)>>1;
build(lc,ln,mid);
build(rc,mid+1,rn);
pushup(p);
}
void update(int p,int x,int v)
{
if(tr[p].l==x&&tr[p].r==x)
{
tr[p].mx=v-x;
tr[p].mi=x+v-x;
return;
}
int mid=(tr[p].l+tr[p].r)>>1;
if(x<=mid)update(lc,x,v);
else update(rc,x,v);
pushup(p);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>q;
for(int i=1;i<=n;i++)cin>>a[i],a[i+n]=a[i];
build(1,1,2*n);
cout<<tr[1].mi+n-1<<endl;
lastans=tr[1].mi+n-1;
while(m--)
{
int x,y;
cin>>x>>y;
if(q)x^=lastans,y^=lastans;
update(1,x,y);
update(1,x+n,y);
cout<<tr[1].mi+n-1<<endl;
lastans=tr[1].mi+n-1;
}
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战