P9447 题解

· · 题解

怎么题解区全是线段树,这个应该好写一些。

题意

### 题解 由于是强制走过线段,若按到中心的距离从大到小考虑,则每条线段相当于交换相邻两条射线的终点编号,要求即最终 $i$ 射线终点为 $s$。考虑按该顺序 DP,设 $f_{i,j}$ 表示考虑了前 $i$ 条给定线段,当前 $j$ 射线终点为 $s$ 的最小加入数,有初始值 $f_{0,i}=\min(\left|s-i\right|,n-\left|s-i\right|)$。 至于转移,先交换 $f_{i-1,t_{i}},f_{i-1,t_{i}\bmod n+1}$。之后还要考虑线段的加入,有 $f'_{i,j}=\min f_{i-1,k}+\min(\left|j-k\right|,n-\left|j-k\right|)$。由于只有两个位置变化,只需要尝试用较小值向外更新,并尝试更新较大值,可以做到单次 $\mathcal O(n)$,总复杂度 $\mathcal O(nm)$,答案即 $f_{m,i}$。 观察这个 DP 的形式,注意到差分数组只有 $-1,0,1$,否则一定可以更新,于是考虑维护差分数组 $g_i=f_{i}-f_{(i+n-2)\bmod n+1}$。设 $x=t_i,y=t_i\bmod n+1$,$g_y=0$ 时交换没有影响,以下以 $g_y=1$ 为例,$g_y=-1$ 是类似的。 首先考虑 $x$ 位置的变化,$f_x$ 先由于交换增加 $1$。此时若 $g_x=1$,则 $f_x$ 还会被上个位置拉回来,于是 $g_x$ 仍为 $1$,$g_y$ 变成 $0$;否则就拉不回来了,$g_x$ 增加 $1$,$g_y$ 变成 $-1$。至于 $y$ 减小对其他位置的影响,显然无法更新前面元素,画图可得从 $y$ 向后找到首个非 $1$ 位置 $z$,此前的 $f$ 值均会减一,在 $g$ 上的影响即 $g_z$ 增加 $1$。 这需要查询 $y$ 后面首个非 $1$ 位置,在 $g_y=-1$ 时是前面首个非 $-1$ 位置,用 set 分别维护两种下标,单点改的时候也修改 set 即可。另外若只考虑给定操作会将 $s$ 交换到 $t$,则一定有 $f_{m,t}=0$,从该位置按 $g$ 数组推出 $f$ 即为答案,复杂度 $\mathcal O(m\log n)$。 ### 代码 ~~~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; } ~~~