不知道为什么 1001 一直 T,可能是现学树剖写出来的板子常数大。不过本地跑非随机数据也只用了 0.58s.
发现了,是查询复杂度不对,犯了低级错误。
1002 C++ to Python
签到。
1007 Snatch Groceries
很长的阅读理解废话,跳过发现就是区间排序,也算签到。
废话骗了很多人
#include <bits/stdc++.h>
#define int long long
const int maxn = 1e5 + 10;
using namespace std;
struct node {
int l, r;
bool operator < (const node &x) const {
if (l == x.l) return r < x.r;
return l < x.l;
}
}a[maxn];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i].l >> a[i].r;
}
a[n + 1].l = 1e9 + 10;
sort(a + 1, a + 1 + n);
int cnt = 0;
for (int i = 1; i <= n; ++i) {
if (a[i].r >= a[i + 1].l) {
break;
}
cnt++;
}
cout << cnt << endl;
}
}
1012 Luxury cruise ship
看上去是入门的 dp 问题,选纸币。但是纸币值是 7 , 31 , 365 7,31,365 7,31,365,数据范围到了 1 e 18 1e18 1e18.
发现 365 = 5 × 31 + 30 × 7 365=5×31+30×7 365=5×31+30×7, 所以优先用 365 365 365 肯定是优于其他情况的。
所以先对 n n n 取模到 [ 0 , 365 ) [0,365) [0,365),然后再作 dp 即可,转移方程 f [ i ] = m i n ( f [ i − 7 ] , f [ i − 31 ] ) + 1 f[i]=min(f[i-7],f[i-31])+1 f[i]=min(f[i−7],f[i−31])+1.
实际上暴力一点,直接对前 7 × 31 × 365 = 79205 7×31×365=79205 7×31×365=79205 个数据预处理也是可以的,完全可接受。想太复杂了。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int a[3] = {7, 31, 365};
int nums[1010];
int main()
{
memset(nums, 0x3f, sizeof(nums));
nums[0] = 0;
for (int i = 0; i < 3; i++)
for (int j = a[i]; j <= 1000; j++)
if (nums[j - a[i]] != 0x3f3f3f3f)
nums[j] = min(nums[j], nums[j - a[i]] + 1);
int t;
cin >> t;
while (t--)
{
ll x;
cin >> x;
if (x < 365)
{
if (nums[x] != 0x3f3f3f3f)
cout << nums[x] << endl;
else
cout << -1 << endl;
}
else
{
ll p = x / 365ll;
ll q = x % 365ll;
if (nums[q] != 0x3f3f3f3f)
cout << p + nums[q] << endl;
else
cout << p - 1 + nums[q + 365] << endl;
}
}
}
(队友的代码)
1009 ShuanQ
观察式子 x = P y % M , y = Q x % M x=Py\ \%\ M,y=Qx\ \% M x=Py % M,y=Qx %M.
其实不用观察
首先由条件 P Q % M = 1 PQ\ \%\ M = 1 PQ % M=1 可知 ( P Q − 1 ) % M = 0 (PQ-1)\ \%\ M=0 (PQ−1) % M=0. 即有
P Q = k M , k = 1 , 2 , . . . PQ=kM,k=1,2,... PQ=kM,k=1,2,...
又由最前面的式子知道, k k k 不可能小于 max { P , Q } \max\{P,Q\} max{P,Q} 的,而且一定是一个质因子。
知道是质因子就好做了,找到一个最大的满足条件的就行。
实际上也很容易证明这样的 k k k 只有一个。
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=2e6+10;
int prime[maxn],cnt=0;
int vis[maxn];
int mnprime[maxn];
void get_prime(int n){
for(int i=2;i<=n;i++){
if(!vis[i])prime[cnt++]=i;
for(int j=0;i*prime[j]<=n;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
}
int main(){
int tt;
get_prime(2000000);
scanf("%d",&tt);
while(tt--){
ll p,q,e;
cin>>p>>q>>e;
ll xx=p*q-1;
vector<long long>v;
v.clear();
for(int i=0;i<cnt;i++){
if(xx%prime[i]==0){
if(prime[i]>max(p,q)){
v.push_back(prime[i]);
}
while(xx%prime[i]==0)xx/=prime[i];
}
}
if(xx!=1)v.push_back(xx);
if(v.size()==0){
printf("shuanQ\n");
}
else{
printf("%lld\n",e*q%v[0]);
}
}
}
(还是队友写的,数论爷 orz)
1001 Static Query on Tree
题意:
给一个树,然后给 q q q 次询问,每次询问给三个节集 A , B , C A,B,C A,B,C,问有多少个点同时满足以下条件:
- 点在 A A A 中某一点到根节点 1 1 1 的简单路径上
- 点在 B B B 中某一点到根节点 1 1 1 的简单路径上
- 点到根节点的简单路径上存在 C C C 中某一点
数据保证 ∑ ( A + B + C ) < 2 e 5 \sum(A+B+C)<2e5 ∑(A+B+C)<2e5.
比赛的时候树剖调出来了,但是一直 TLE. 赛后继续 T.
自己的解法是,将树重链剖分,然后可以在 log \log log 时间内给 C C C 的每个节点的子树打上标记,总共花 O ( C log n ) O(C\log n) O(Clogn)。然后再给 A , B A,B A,B 每个点到根节点上的各点打上标记。由于重链剖分的性质,这个操作最多也就是 O ( ( A + B ) log 2 n ) O((A+B)\log^2n) O((A+B)log2n) 的。最后再询问整个树中哪些点被打上了三个标记, O ( log n ) O(\log n) O(logn).
总复杂度
O
(
(
n
+
C
+
(
A
+
B
)
log
n
)
log
n
)
O((n+C+(A+B)\log n)\log n)
O((n+C+(A+B)logn)logn),我觉得不应该 TLE((( 事后看题解发现官方题解也用了这个方法,和我一模一样。教教我怎么写出常数小的树剖 qwq.
上面一段话完全是没有意识到自己怎么错了。
请看我的查询:
int exist(int k, int l, int r) {
if (a[k].l > r || a[k].r < l) return 0;
if (a[k].l >= l && a[k].r <= r) {
if (a[k].v1 == a[k].r - a[k].l + 1 && a[k].v2 == a[k].r - a[k].l + 1 && a[k].r - a[k].l + 1 == a[k].v3)
return a[k].r - a[k].l + 1;
}
if (a[k].l == a[k].r) return 0;
pushdown(k);
return (exist(lson, l, r) + exist(rson, l, r));
}
很明显即使到了范围内也没有强制 return
,相当于没有用懒标记, TLE 是很简单的事。
修改查询?Query 子树中有多少个点满足某个条件.
比如这里我们要的条件是满足同时具有 A , B , C A,B,C A,B,C 三个标记,那么我存的时候换一种形式,三个变量分别存:被 A A A 标记的点,被 A B AB AB 标记的点,被 A B C ABC ABC 标记的点。那么我首先用集合 A A A 更新,然后用 B B B 更新,更新的时候 A B AB AB 标记个数 = 区间内 A A A 的个数(而不是区间长度)× 下传的值,最后用 C C C 更新, A B C ABC ABC 标记个数 = 区间内 A B AB AB 标记个数 ×下传值.
改完就过了
#include <bits/stdc++.h>
#define lson k << 1
#define rson k << 1 | 1
#define int long long
const int maxn = 2e5 + 10;
using namespace std;
int fa[maxn], dep[maxn], siz[maxn], son[maxn], top[maxn], dfn[maxn];
int cnt;
int n, q;
int ce;
vector<int> e[maxn];
void poufen1(int o, int f, int d) {
fa[o] = f;
dep[o] = d;
siz[o] = 1;
for (int i = 0; i < e[o].size(); ++i) {
int x = e[o][i];
if (x == f) continue;
poufen1(x, o, d + 1);
siz[o] += siz[x];
if (siz[x] > siz[son[o]]) son[o] = x;
}
}
void poufen2(int o, int t) {
dfn[o] = ++cnt;
top[o] = t;
if (!son[o]) return;
poufen2(son[o], t);
for (int i = 0; i < e[o].size(); ++i) {
int x = e[o][i];
if (x != son[o] && x != fa[o]) poufen2(x, x);
}
}
struct node {
int l, r;
int v1, v2, v3;
int t1, t2, t3;
}a[maxn << 2];
void pushup(int k) {
a[k].v1 = a[lson].v1 + a[rson].v1;
a[k].v2 = a[lson].v2 + a[rson].v2;
a[k].v3 = a[lson].v3 + a[rson].v3;
}
void build(int k, int l, int r) {
a[k].l = l, a[k].r = r;
a[k].t1 = a[k].t2 = a[k].t3 = -1;
a[k].v1 = a[k].v2 = a[k].v3 = 0;
if (l == r) return;
int mid = l + r >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
// pushup(k);
}
void pushdown(int k) {
int lenl = a[lson].r - a[lson].l + 1, lenr = a[rson].r - a[rson].l + 1;
if (a[k].t1 != -1) {
a[lson].t1 = a[k].t1;
a[rson].t1 = a[k].t1;
a[lson].v1 = a[k].t1 * lenl;
a[rson].v1 = a[k].t1 * lenr;
a[k].t1 = -1;
}
if (a[k].t2 != -1) {
a[lson].t2 = a[k].t2;
a[rson].t2 = a[k].t2;
a[lson].v2 = a[k].t2 * a[lson].v1;
a[rson].v2 = a[k].t2 * a[rson].v1;
a[k].t2 = -1;
}
if (a[k].t3 != -1) {
a[lson].t3 = a[k].t3;
a[rson].t3 = a[k].t3;
a[lson].v3 = a[k].t3 * a[lson].v2;
a[rson].v3 = a[k].t3 * a[rson].v2;
a[k].t3 = -1;
}
}
void add(int k, int l, int r, int x, int val) {
if (a[k].l > r || a[k].r < l) return;
if (a[k].l >= l && a[k].r <= r) {
if (x == 1) a[k].v1 = val * (a[k].r - a[k].l + 1), a[k].t1 = val;
else if(x == 2) a[k].v2 = val * a[k].v1, a[k].t2 = val;
else a[k].v3 = val * a[k].v2, a[k].t3 = val;
return;
}
pushdown(k);
add(lson, l, r, x, val);
add(rson, l, r, x, val);
pushup(k);
}
void up_add(int i, int x, int val) {
while (top[i]) {
add(1, dfn[top[i]], dfn[i], x, val);
i = fa[top[i]];
}
}
int exist(int k, int l, int r) {
if (a[k].l > r || a[k].r < l) return 0;
if (a[k].l >= l && a[k].r <= r) {
return a[k].v3;
}
pushdown(k);
return (exist(lson, l, r) + exist(rson, l, r));
}
int A[maxn], B[maxn], C[maxn];
int ca, cb, cc;
signed main()
{
// clock_t t_start, t_end;
// t_start = clock();
// freopen("1001.in", "r", stdin);
// freopen("1001.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
int t;
cin >> t;
while (t--) {
cnt = 0;
cin >> n;
cin >> q;
for (int i = 2; i <= n; ++i) {
int r;
cin >> r;
e[r].push_back(i);
e[i].push_back(r);
}
poufen1(1, -1, 0);
poufen2(1, 1);
top[1] = 0;
build(1, 1, n);
while (q--) {
cin >> ca >> cb >> cc;
for (int i = 1; i <= ca; ++i) cin >> A[i];
for (int i = 1; i <= cb; ++i) cin >> B[i];
for (int i = 1; i <= cc; ++i) cin >> C[i];
for (int i = 1; i <= ca; ++i) {
up_add(A[i], 1, 1);
}
for (int i = 1; i <= cb; ++i) {
up_add(B[i], 2, 1);
}
for (int i = 1; i <= cc; ++i) {
add(1, dfn[C[i]], dfn[C[i]] + siz[C[i]] - 1, 3, 1);
}
int ans = 0;
cout << exist(1, 1, n) << endl;
add(1, 1, n, 1, 0);
add(1, 1, n, 2, 0);
add(1, 1, n, 3, 0);
}
}
// t_end = clock();
// cout << (double)(t_end - t_start) / CLOCKS_PER_SEC << endl;
return 0;
}
/*
1
7 3
1 1 2 2 3 3
4 4 3
4 5 6 7
4 5 6 7
2 4 6
*/
1003 Copy
题意
给一个数组,有多次操作,每次可以选择一个区间,然后复制这个区间,把其插入到原来区间的后面,其他后面的数顺延。除了这种复制操作,还有询问操作会问位置 x x x 有什么数字。
保证 n ≤ 1 e 5 n\leq1e5 n≤1e5,选择的区间,询问的位置都小于 n n n,但复制出来的整个数组大小不会小于 n n n. (不过后面的数直接丢掉就可以了)
思路
逆向离线。因为每次复制操作对于那些在复制之前产生的询问,以及在复制之后但询问位置小于区间的 l l l 是无效的,所以我们可以倒着处理询问,每次遇到复制操作,就把复制操作之后出现的所有 [ r , + ∞ ) [r,+\infin) [r,+∞) 询问的值减去 r − l + 1 r-l+1 r−l+1,然后处理一下就可以了。
这样我们得到一个 O ( n 2 ) O(n^2) O(n2) 的解法。可以对询问做带区间操作的数据结构来优化,也可以用 b i t s e t bitset bitset 卡过去。
卡法:
因为我们只需要求出最后答案的异或,也就是说一个位置被询问奇数次才有效。所以只需要把所有询问也取异或即可。考虑 b i t s e t bitset bitset 维护,最终值为 1 就把答案异或上该位置的值。然后逆向处理询问,每次出现修改操作,就把所有出现在修改操作左侧的询问全部右移 r − l + 1 r-l+1 r−l+1,然后把这些值异或到修改操作右侧的地方去。
#include <bits/stdc++.h>
// #pragma GCC optimize(2)
// #define int long long
using namespace std;
const int maxn = 1e5 + 10;
int n, q;
bitset<maxn> b, c, d;
int a[maxn];
int op[maxn], l[maxn], r[maxn];
signed main()
{
// freopen("1003.in", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--) {
b.reset();
cin >> n >> q;
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= q; ++i) {
cin >> op[i] >> l[i];
if (op[i] == 1) {
cin >> r[i];
}
}
for (int i = q; i >= 1; --i) {
if (op[i] == 2) {
b[l[i]] = b[l[i]] - 1;
}
else {
c = b & (~bitset<maxn>(0) >> (maxn - r[i] - 1));
d = b & (~bitset<maxn>(0) << (r[i] + 1));
b = c ^ (d >> (r[i] - l[i] + 1));
}
}
int ans = 0;
for (int i = 1; i <= n; ++i) {
if (b[i]) ans ^= a[i];
}
cout << ans << endl;
}
return 0;
}
1011 DOS Card
题意:现在有一个值互不相同的数组,维护区间的这个值:
选择区间中四个不同位置 a , b , c , d a,b,c,d a,b,c,d, 要求 a > b , c > d a>b,c>d a>b,c>d, a 2 − b 2 + c 2 − d 2 a^2 - b^2 + c^2 - d^2 a2−b2+c2−d2 的最大值。也就是要选两对有序对使得平方差的和最大。首先先把原数组每个数做平方,然后就是选两对有序对使得差的和最大。
可以尝试先考虑一对数,然后再通过一对维护出第二对的答案。
题解说维护八个值:
区间最大值、区间最小值、次大值、次小值
选一对的最大值(
a
2
−
b
2
a^2 - b^2
a2−b2),选一对后加上另外一个区间中的数的最大值 (
a
2
−
b
2
+
c
2
a^2 - b^2+c^2
a2−b2+c2),选一对后减去另外一个区间中的数的最小值 (
a
2
−
b
2
−
d
2
a^2-b^2-d^2
a2−b2−d2)。
那么我们可以发现:
选一对的最大值=max({左子树选一对,右子树选一对,左子树最大值-右子树最小值})
选一对后加上另外一个区间中的数的最大值=max({左子树答案, 右子树答案, 左子树选一对+右子树最大值,左子树最大值+右子树选一对, 左子树最大值-右子树最小值+左子树次大值, 左子树最大值-右子树最小值+右子树最大值})
选一对后减去另外一个区间中的数的最小值=max({左子树答案,右子树答案, 左子树选一对-右子树最小值, -左子树最小值+右子树选一对, 左子树最大值-右子树最小值-左子树最小值, 左子树最大值-右子树最小值-右子树次小值})
选两队的最大值=max({左子树答案, 右子树答案, 左子树选一对+右子树选一对, 左子树选一对后加上最大值-右子树最小值, 左子树最大值+右子树选一对后减去最小值, 左子树最大值+左子树次大值-右子树最小值-右子树次小值})
维护一下就可以了(
(第一次写维护像这样复杂的线段树,代码很臭)
#include <bits/stdc++.h>
#define int long long
#define lson k << 1
#define rson k << 1 | 1
using namespace std;
const int inf = 1e18;
const int maxn = 2e5 + 10;
int n;
struct node {
int l, r;
int mx, mn, smx, smn;
int pair1, pair2, pair1_mx, pair1_mn;
}a[maxn << 2];
int num[maxn];
void pushup(int k) {
a[k].mx = max(a[lson].mx, a[rson].mx);
a[k].mn = min(a[lson].mn, a[rson].mn);
if (a[lson].smx > a[rson].mx) a[k].smx = a[lson].smx;
else if (a[rson].smx > a[lson].mx) a[k].smx = a[rson].smx;
else a[k].smx = min(a[lson].mx, a[rson].mx);
if (a[lson].smn < a[rson].mn) a[k].smn = a[lson].smn;
else if (a[rson].smn < a[lson].mn) a[k].smn = a[rson].smn;
else a[k].smn = max(a[lson].mn, a[rson].mn);
a[k].pair1 = max({a[lson].pair1, a[rson].pair1, a[lson].mx - a[rson].mn});
a[k].pair2 = max(a[lson].pair2, a[rson].pair2);
a[k].pair2 = max(a[k].pair2, a[lson].pair1 + a[rson].pair1);
a[k].pair2 = max(a[k].pair2, a[lson].pair1_mx - a[rson].mn);
a[k].pair2 = max(a[k].pair2, a[lson].mx + a[rson].pair1_mn);
a[k].pair2 = max(a[k].pair2, a[lson].mx + a[lson].smx - a[rson].mn - a[rson].smn);
a[k].pair1_mx = max(a[lson].pair1_mx, a[rson].pair1_mx);
a[k].pair1_mx = max(a[k].pair1_mx, a[lson].pair1 + a[rson].mx);
a[k].pair1_mx = max(a[k].pair1_mx, a[lson].mx + a[rson].pair1);
if (a[lson].r - a[lson].l + 1 >= 2) a[k].pair1_mx = max(a[k].pair1_mx, a[lson].mx - a[rson].mn + a[lson].smx);
if (a[rson].r - a[rson].l + 1 >= 2) a[k].pair1_mx = max(a[k].pair1_mx, a[lson].mx - a[rson].mn + a[rson].mx);
a[k].pair1_mn = max(a[lson].pair1_mn, a[rson].pair1_mn);
a[k].pair1_mn = max(a[k].pair1_mn, a[rson].pair1 - a[lson].mn);
a[k].pair1_mn = max(a[k].pair1_mn, a[lson].pair1 - a[rson].mn);
if (a[lson].r - a[lson].l + 1 >= 2) a[k].pair1_mn = max(a[k].pair1_mn, a[lson].mx - a[rson].mn - a[lson].mn);
if (a[rson].r - a[rson].l + 1 >= 2) a[k].pair1_mn = max(a[k].pair1_mn, a[lson].mx - a[rson].mn - a[rson].smn);
}
void build(int k, int l, int r) {
a[k].l = l, a[k].r = r;
if (l == r) {
a[k].mx = a[k].mn = num[l] * num[l];
a[k].smn = inf, a[k].smx = -inf;
a[k].pair1_mx = a[k].pair1 = a[k].pair2 = a[k].pair1_mn = -inf;
return;
}
int mid = l + r >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
pushup(k);
}
node query(int k, int l, int r) {
if (a[k].l > r || a[k].r < l) return {-1, -1, -inf, inf, -inf, inf, -inf, -inf, -inf, -inf};
if (a[k].l >= l && a[k].r <= r) return a[k];
// return max({a[lson].pair2, a[rson].pair2, a[lson].pair1 + a[rson].pair1, a[lson].pair1_mx + a[rson].mn, a[lson].mx + a[rson].pair1_mn});
node n1 = query(lson, l, r);
node n2 = query(rson, l, r);
node ret = {-1, -1, -inf, inf, -inf, inf, -inf, -inf, -inf, -inf};
ret.l = max(l, a[k].l);
ret.r = min(r, a[k].r);
ret.mx = max(n1.mx, n2.mx);
ret.mn = min(n1.mn, n2.mn);
if (n1.smx > n2.mx) ret.smx = n1.smx;
else if (n2.smx > n1.mx) ret.smx = n2.smx;
else ret.smx = min(n1.mx, n2.mx);
if (n1.smn < n2.mn) ret.smn = n1.smn;
else if (n2.smn < n1.mn) ret.smn = n2.smn;
else ret.smn = max(n1.mn, n2.mn);
ret.pair1 = max({n1.pair1, n2.pair1, n1.mx - n2.mn});
ret.pair2 = max(n1.pair2, n2.pair2);
ret.pair2 = max(ret.pair2, n1.pair1 + n2.pair1);
ret.pair2 = max(ret.pair2, n1.pair1_mx - n2.mn);
ret.pair2 = max(ret.pair2, n1.mx + n2.pair1_mn);
ret.pair2 = max(ret.pair2, n1.mx + n1.smx - n2.mn - n2.smn);
ret.pair1_mx = max(n1.pair1_mx, n2.pair1_mx);
ret.pair1_mx = max(ret.pair1_mx, n1.pair1 + n2.mx);
ret.pair1_mx = max(ret.pair1_mx, n1.mx + n2.pair1);
if (n1.r - n1.l + 1 >= 2) ret.pair1_mx = max(ret.pair1_mx, n1.mx - n2.mn + n1.smx);
if (n2.r - n2.l + 1 >= 2) ret.pair1_mx = max(ret.pair1_mx, n1.mx - n2.mn + n2.mx);
ret.pair1_mn = max(n1.pair1_mn, n2.pair1_mn);
ret.pair1_mn = max(ret.pair1_mn, n2.pair1 - n1.mn);
ret.pair1_mn = max(ret.pair1_mn, n1.pair1 - n2.mn);
if (n1.r - n1.l + 1 >= 2) ret.pair1_mn = max(ret.pair1_mn, n1.mx - n2.mn - n1.mn);
if (n2.r - n2.l + 1 >= 2) ret.pair1_mn = max(ret.pair1_mn, n1.mx - n2.mn - n2.smn);
// if (n1.l == 0 && n1.r == 0) ret = n2;
// if (n2.l == 0 && n2.r == 0) ret = n1;
return ret;
}
// 区间询问:从区间 [L,R] 选择四个不同坐标 i,j<k,l a[i]^2+a[j]^2-a[k]^2-a[l]^2 的最大值
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
// freopen("1011.in", "r", stdin);
// freopen("1011.out", "w", stdout);
// 区间 ai*ai-aj*aj
// 选了两对的最大值=max({左边选2对,右边选两队,左边选一对+左边剩下最大值-右边剩下最小值,右边选一对+左边剩下最大值-右边剩下最小值,左边剩下最大值+左边剩下次大值-右边剩下最小值-右边剩下次小值})
// 最大值次大值:模板
// 选了一对的最大值=max({左边选一对的最大值,右边选一对的最大值,左边选最大值-右边选最小值})
// 选了一对最大的和剩下的最大值=max({左边的值,右边的值,左边一对+右边最大值,右边一对+左边最大值,左最大-右最小+max(左次大,右最大)})
// 最小值同上
int t;
cin >> t;
while (t--) {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) cin >> num[i];
build(1, 1, n);
while (m--) {
int l, r;
cin >> l >> r;
cout << query(1, l, r).pair2 << endl;
}
}
return 0;
}