cf331 B2. Shave Beaver!(树状数组)
题意:
给定一个1~n的排列,q个询问,询问有两种类型:
1 x y:问至少几次能把 \(x\sim y\) 全部打一遍(\(1\le x<y\le n\))。每次能打一个连续上升子序列。注意x和y是值而非位置/下标,这里的打不会改变数组中的任何数,也就是不会影响后续操作;
2 i j:交换 \(a_i\) 和 \(a_j\) 的值。注意 i 和 j 是下标而非值。
\(n\le 3e5, q\le 1e5\)
思路:
突破口:判断每一个值x是不是包含x的连续上升子列的最右端点(下面简称 “右端” ),在值域上开树状数组记录。
对于询问,输出区间中有几个右端。这相当于把区间\([x,y]\) 切成若干段 \([x,k_1][k_1+1,k_2]\cdots [k_m+1,y]\) ,每次打一段。前面的区间的右端都能正确计入,但注意最后一个区间 \([k_m+1,y]\) 的右端可能大于 \(y\) 从而未被计入,因此要判断一下 \(y\) 是不是右端。
对于交换,改变某值x的位置只会影响到x-1、x、x+1三个值,先换位再分情况讨论即可。
需要单点修改、区间加,用树状数组即可。
const int N = 3e5 + 5;
int n, q, a[N], pos[N];
int tr[N];
int lowbit(int x) {return x&-x; }
void add(int p, int x) {for(;p<=n;p+=lowbit(p))tr[p]+=x; }
int ask(int p) {int s=0;for(;p;p-=lowbit(p))s+=tr[p];return s; }
int ask(int l, int r) {return ask(r)-ask(l-1); }
void update(int i, int j) //i+1=j
{
if(i < 1 || j > n) return;
if(ask(i, i)) {
if(pos[i] < pos[j]) add(i, -1);
}
else {
if(pos[i] > pos[j]) add(i, 1);
}
}
signed main()
{
iofast;
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i], pos[a[i]] = i;
for(int i = 1; i <= n; i++)
if(i == n || pos[i] > pos[i+1]) add(i, 1);
cin >> q; while(q--)
{
int t, x, y; cin >> t >> x >> y;
if(t == 1) cout << ask(x, y) + (ask(y, y)?0:1) << endl;
else
{
pos[a[x]] = y, pos[a[y]] = x; //换位置
update(a[x]-1, a[x]), update(a[x], a[x]+1);
update(a[y]-1, a[y]), update(a[y], a[y]+1);
swap(a[x], a[y]); //原数组也换一下
}
}
}

浙公网安备 33010602011771号