[USACO23JAN] Tractor Paths P 题解
题意:
- 有
n 个区间[l_i,r_i] ,满足l_1<l_2<\dots<l_n ,r_1<r_2<\dots<r_n ,且\forall\ 2\le i\le n 都有l_i<r_{i-1} 。其中,一部分区间是关键区间。 - 一个区间可以传送到另一个区间,当且仅当两区间相交。
-
1. 从 $a$ 到 $b$ 至少需要传送几次。 2. 在保证传送次数最少的前提下,有多少个关键区间可以在此过程中被传送到。 -
先将所有区间按左端点(或右端点)从小到大排序。
对于第一问,一个显然的贪心是每次选取与当前区间相交的最靠右的区间,这启发我们建出一棵树:树上的
我们的贪心对应的树上操作就是从
对于第二问,我们首先需要明确每次从某个结点能跳到哪些结点。不难发现,对于一个结点
考虑建树的过程,我们每次按编号从小到大加入结点,可以发现当加入一个结点时,其所有儿子的结点编号必然是当前所有暂无父亲的结点编号中最小的若干个(可以为零个)。由此我们可以证明,对于所有深度相同的结点编号,以及一个结点所有儿子的结点编号,它们都必然构成一段连续的区间,而这也使这棵树拥有了极好的性质。
接下来我们以下图这棵树为例(当然实际情况中树的形态可以千奇百怪,这里为了美观画了一棵对称的树):
形象化地,若
回到原问题,我们分两种情况讨论。
1.
由于每次操作只能使当前结点的深度减少
以
不难发现,绿色结点的编号在
2.
同样地,通过分析深度变化可知,我们需恰好进行一次向右跳的操作,其余操作均为向左上跳。
以
不难发现,蓝色结点的编号在
至此,我们便用
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,q,x,y,L,tmp,timer,l[200009],r[200009],dfn[200009],dis[200009],ans[200009],bit[200009],anc[200009][20];
char d[400009],s[200009];
struct st{ll x,w,id;};
vector <ll> to[200009];
vector <st> qr[200009];
inline ll read(){
ll s = 0,w = 1;
char ch = getchar();
while (ch > '9' || ch < '0'){ if (ch == '-') w = -1; ch = getchar();}
while (ch <= '9' && ch >= '0') s = (s << 1) + (s << 3) + (ch ^ 48),ch = getchar();
return s * w;
}
void upd(ll x,ll y){for (;x <= n;x += x & (-x)) bit[x] += y;}
ll query(ll x){ll res = 0; for (;x;x -= x & (-x)) res += bit[x]; return res;}
void build(){
ll cl = 0,cr = 0,pos = 1;
for (ll i = 1;i <= 2 * n;i += 1){
if (d[i] == 'L') l[++ cl] = i;
else r[++ cr] = i;
}
for (ll i = 2;i <= n;i += 1){
while (pos < i && (i == n || l[i + 1] > r[pos])){
anc[pos][0] = i;
to[i].emplace_back(pos ++);
}
}
}
void dfs(ll u){
dfn[u] = ++ timer;
for (ll i = 1;i <= L;i += 1) anc[u][i] = anc[anc[u][i - 1]][i - 1];
for (ll i = 0;i < to[u].size();i += 1) dfs(to[u][i]);
}
void add(ll l,ll r,ll L,ll R,ll id){
qr[r].emplace_back((st){R,1,id});
qr[r].emplace_back((st){L - 1,-1,id});
qr[l - 1].emplace_back((st){R,-1,id});
qr[l - 1].emplace_back((st){L - 1,1,id});
}
void work(){
for (ll i = 1;i <= n;i += 1){
if (s[i] == '1') upd(dfn[i],1);
for (ll j = 0;j < qr[i].size();j += 1){
ans[qr[i][j].id] += query(qr[i][j].x) * qr[i][j].w;
}
}
}
int main(){
n = read(),q = read(),L = log2(n),scanf("%s",d + 1),scanf("%s",s + 1);
build(),dfs(n);
for (ll i = 1;i <= q;i += 1){
x = read(),y = read(),dis[i] = 1;
ll tmp = x;
for (ll j = L;j >= 0;j -= 1){
if (!anc[x][j] || anc[x][j] >= y) continue;
dis[i] += 1 << j,x = anc[x][j];
}
if (dfn[tmp] < dfn[y]) add(tmp,y,1,dfn[tmp],i),add(tmp,y,dfn[y],n,i);
else add(tmp,y,dfn[y],dfn[tmp],i);
}
work();
for (ll i = 1;i <= q;i += 1) printf("%lld %lld\n",dis[i],ans[i]);
return 0;
}