2022“杭电杯”中国大学生算法设计超级联赛(2)个人题解

不知道为什么 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[i7],f[i31])+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 (PQ1) % 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,问有多少个点同时满足以下条件:

  1. 点在 A A A 中某一点到根节点 1 1 1 的简单路径上
  2. 点在 B B B 中某一点到根节点 1 1 1 的简单路径上
  3. 点到根节点的简单路径上存在 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 n1e5,选择的区间,询问的位置都小于 n n n,但复制出来的整个数组大小不会小于 n n n. (不过后面的数直接丢掉就可以了)

思路

逆向离线。因为每次复制操作对于那些在复制之前产生的询问,以及在复制之后但询问位置小于区间的 l l l 是无效的,所以我们可以倒着处理询问,每次遇到复制操作,就把复制操作之后出现的所有 [ r , + ∞ ) [r,+\infin) [r,+) 询问的值减去 r − l + 1 r-l+1 rl+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 rl+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 a2b2+c2d2 的最大值。也就是要选两对有序对使得平方差的和最大。首先先把原数组每个数做平方,然后就是选两对有序对使得差的和最大。

可以尝试先考虑一对数,然后再通过一对维护出第二对的答案。

题解说维护八个值:

区间最大值、区间最小值、次大值、次小值
选一对的最大值( a 2 − b 2 a^2 - b^2 a2b2),选一对后加上另外一个区间中的数的最大值 ( a 2 − b 2 + c 2 a^2 - b^2+c^2 a2b2+c2),选一对后减去另外一个区间中的数的最小值 ( a 2 − b 2 − d 2 a^2-b^2-d^2 a2b2d2)。

那么我们可以发现:

选一对的最大值=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;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值