专栏文章
P9447 题解
P9447题解参与者 5已保存评论 4
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 4 条
- 当前快照
- 1 份
- 快照标识符
- @min460th
- 此快照首次捕获于
- 2025/12/01 20:16 3 个月前
- 此快照最后确认于
- 2025/12/01 20:16 3 个月前
怎么题解区全是线段树,这个应该好写一些。
题意
条射线绕中心形成一个环,有 条连接相邻两射线的线段,两端点到中心距离相等。初始选择某射线向外走,遇到线段时强制走到另一端。可以任意加入满足同样限制的线段,对每个 求初始在 射线上走,最终走到 射线无穷远处需要加入的最少线段数。。
题解
由于是强制走过线段,若按到中心的距离从大到小考虑,则每条线段相当于交换相邻两条射线的终点编号,要求即最终 射线终点为 。考虑按该顺序 DP,设 表示考虑了前 条给定线段,当前 射线终点为 的最小加入数,有初始值 。
至于转移,先交换 。之后还要考虑线段的加入,有 。由于只有两个位置变化,只需要尝试用较小值向外更新,并尝试更新较大值,可以做到单次 ,总复杂度 ,答案即 。
观察这个 DP 的形式,注意到差分数组只有 ,否则一定可以更新,于是考虑维护差分数组 。设 , 时交换没有影响,以下以 为例, 是类似的。
首先考虑 位置的变化, 先由于交换增加 。此时若 ,则 还会被上个位置拉回来,于是 仍为 , 变成 ;否则就拉不回来了, 增加 , 变成 。至于 减小对其他位置的影响,显然无法更新前面元素,画图可得从 向后找到首个非 位置 ,此前的 值均会减一,在 上的影响即 增加 。
这需要查询 后面首个非 位置,在 时是前面首个非 位置,用 set 分别维护两种下标,单点改的时候也修改 set 即可。另外若只考虑给定操作会将 交换到 ,则一定有 ,从该位置按 数组推出 即为答案,复杂度 。
代码
CPP#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,m,s,f[N],g[N]; pair<int,int> a[N];
set <int> sa,sb;
void cg(int p,int x)
{
if(g[p]==1&&x!=1) sa.insert(p);
if(g[p]!=1&&x==1) sa.erase(p);
if(g[p]==-1&&x!=-1) sb.insert(p);
if(g[p]!=-1&&x==-1) sb.erase(p);
g[p]=x;
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m>>s;
for(int i=1;i<=m;i++) cin>>a[i].first>>a[i].second;
for(int i=1;i<=n;i++) f[i]=min(abs(i-s),n-abs(i-s));
sort(a+1,a+1+m);
for(int i=1;i<=n;i++)
{
g[i]=f[i]-f[(i+n-2)%n+1];
if(g[i]!=1) sa.insert(i);
if(g[i]!=-1) sb.insert(i);
}
for(int _=m;_;_--)
{
int x=a[_].second,y=x%n+1;
if(s==x||s==y) s=(s^x^y);
if(!g[y]) continue;
if(g[y]==1)
{
int z=-1; auto it=sa.upper_bound(y);
if(it!=sa.end()) z=*it;
else z=*sa.begin();
cg(z,g[z]==0);
if(g[x]==1) cg(y,0);
else cg(y,-1),cg(x,g[x]==0);
}
else
{
int z=-1; auto it=sb.lower_bound(y);
if(it!=sb.begin()) z=*prev(it,1);
else z=*prev(sb.end(),1);
cg(z,g[z]?0:-1),z=y%n+1;
if(g[z]==-1) cg(y,0);
else cg(y,1),cg(z,g[z]?0:-1);
}
}
f[s]=0;
for(int i=s;i>1;i--) f[i-1]=f[i]-g[i];
for(int i=s+1;i<=n;i++) f[i]=f[i-1]+g[i];
for(int i=1;i<=n;i++) cout<<f[i]<<'\n';
return 0;
}
相关推荐
评论
共 4 条评论,欢迎与作者交流。
正在加载评论...