杭电多校2022.7.21第二场部分题解

C Copy

题意:复制一段区间,然后把这一段区间放到原来区间的后面,多次询问求第k位的数,输出所有询问的数的异或和。

题解:加入复制的[l,r]区间,那么对于接下来x>r的询问相当于整体向右移动了r - l + 1的距离,如果我们考虑正向枚举询问,那么对于每次修改都需要顾及到后面的询问。因此我们考虑将操作离线,从后往前枚举。如果是询问我们直接记录该点。如果是区间操作,我们以x>r为分界线,对于所有x>r的后面的询问我们要往前移动r - l + 1的距离。但是这样移动时间复杂度也是n方的,如何优化呢?

这道题的特殊性在于输出的是异或和。异或和相当于一个位置查询两次会抵消,因此一个位置的只需要取0或1,因此我们可以用bitset进行优化。对于一个询问,我们对该位置上的f[i] = !f[i],对于一个修改,我们对他后面的询问进行修改,将f分成前[1 , r]和[r + 1 , n]两部分,后面一部分右移r - l + 1单位长度。最后统计答案时对f上1的位置取值累计即可。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <bitset>

using namespace std;

#define quick_cin() cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
typedef pair<double, double> PDD;

const int N = 1e5 + 10;
int n, m, a[N];
int q[N][3];
bitset<N> f, low, high;

void solve()
{
    f.reset();
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    
    for (int i = 1; i <= m; i ++ )
    {
        cin >> q[i][0];
        if (q[i][0] == 1)//复制区间
            cin >> q[i][1] >> q[i][2];
        else
            cin >> q[i][1];
    }
    
    for (int i = m; i >= 1; i -- )
    {
        if (q[i][0] == 1)
        {
            int l = q[i][1], r = q[i][2];
            low = f & (~bitset<N>(0) >> (N - r - 1));//把r+1位取出来,因为0位不考虑,故r+1
            high = f & (~bitset<N>(0) << (r + 1));//把r+2 ~ N位取出来
            f = low ^ (high >> (r - l + 1));
        }
        else if (q[i][0] == 2)
        {
            int idx = q[i][1];
            f[idx] = !f[idx];
        }
    }
    
    int res = 0;
    for (int i = 1; i <= n; i ++ )
        if (f[i])
            res ^= a[i];
    cout << res << endl;
}

int main()
{
    int T = 1;
    cin >> T;
    while(T -- ) solve();
    return 0;
}

I ShuanQ

题意:给定一个P和Q,以及加密数据data,求加密前的原始数据row。M是质数,PQ的约束条件是Q= 1 / P,P × Q ≡ 1 mod M加密和解密公式如图:

Encryption formula: encrypted_data = raw_data × P mod M

Decryption formula: raw_data = encrypted_data × Q mod M

题解:本题和逆元无关,可以假定KM = P × Q - 1,而M是KM的最大质因数,题目还严格规定M > P && M > Q。故本题寻找KM的最大质因数,若其大于P和M则成立,输出答案,具体代码如下。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <bitset>

using namespace std;

#define quick_cin() cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
typedef pair<double, double> PDD;

const int N = 2000010, INF = 0x3f3f3f3f;
const double eps = 1e-8;
LL P, Q, M, KM, EN;
LL res;

void solve()
{
    res = -1;
    cin >> P >> Q >> EN;
    KM = P * Q - 1;
    
    for (LL i = 2; i * i <= KM; i ++ )
    {
        if (KM % i) continue;
        
        while (KM % i == 0) KM /= i;
        M = i;//每次M获得的数都是质数
    }
    
    if (KM) M = KM;
    if (M > P && M > Q) cout << EN * Q % M << endl;
    else puts("shuanQ");

}


int main()
{
    int T = 1;
    cin >> T;
    while (T -- )
    {
        solve();
    }

    return 0;
}

K DOS Card

题意:给定一段n的数组,每次询问给定一段区间,在区间内选择四个坐标i < j < k < l ,使得四个坐标直接两两匹配,对于每个匹配计算(a[i] + a[j]) * (a[i] - a[j])的贡献,求两个匹配的贡献和的最大值。

分析:简单来说就是区间内找4个数,两两配对,计算a[i] ^ 2 - a[j] ^ 2。因为每个数原来的值没啥用,我们直接存储a[i] ^ 2 即可。为了取到最大值,我们希望前者尽可能大,后者尽可能小。我们考虑用线段树维护。因为有四个数,而且下标必须是严格递增。我们进行分类讨论:

第一种情况 + + - - 在线段树中我们要先维护这两类的最大值
第二种情况 + - + - 
那么这两种情况需要什么来进行转移呢?
我们发现第一种情况 + + - - 可以由三种情况转移而来
1 + + 与 - -
2 + 与 + - -
3 + + - 与 -
因此我们维护 + , - ,+ + , - - ,+ + - , + - - 即可
是不是很简单呢? 我们枚举一下其实也就12种
struct Node // 线段树结构体
{
    int l, r;
    int ans1, ans2; // ++-- +-+-
    int a, b; // + -
    int c, d, e, f; // ++ +- -+ -- 
    int g, h, i, j; // +-- ++- +-+ -+-
}tr[N << 2];
我们只需要维护这12个值即可,在建树的时候只需要变a和b,其他全都由pushup操作完成。

这个线段树其实除了pushup操作以外都没有任何难度,甚至没有修改操作,我们直接看代码。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
#define int long long
const int N = 2e6 + 10, INF = 1e18;
int n, m, q, w[N];
struct Node
{
    int l, r;
    int ans1, ans2; // ++-- +-+-
    int a, b; // + -
    int c, d, e, f; // ++ +- -+ -- 
    int g, h, i, j; // +-- ++- +-+ -+-
    void init(int _l, int _r)
    {
        l = _l, r = _r;
        ans1 = ans2 = a = b = c = d = e = f = g = h = i = j = -INF;
    }
}tr[N << 2];

void pushup(Node &u, const Node& l, const Node& r)
{
    // 每个元素先继承左右儿子的最大值
    u.ans1 = max(l.ans1, r.ans1); 
    u.ans2 = max(l.ans2, r.ans2);
    u.a = max(l.a, r.a);
    u.b = max(l.b, r.b);
    u.c = max(l.c, r.c);
    u.d = max(l.d, r.d);
    u.e = max(l.e, r.e);
    u.f = max(l.f, r.f);
    u.g = max(l.g, r.g);
    u.h = max(l.h, r.h);
    u.i = max(l.i, r.i);
    u.j = max(l.j, r.j);
    
    // 先更新 ++-- +-+- 每个三种情况
    u.ans1 = max(u.ans1, l.a + r.g);
    u.ans1 = max(u.ans1, l.c + r.f);
    u.ans1 = max(u.ans1, l.h + r.b);
    u.ans2 = max(u.ans2, l.a + r.j);
    u.ans2 = max(u.ans2, l.d + r.d);
    u.ans2 = max(u.ans2, l.i + r.b);
    
    //一个个枚举,代码奇葩但是其实思路很简答。
    u.c = max(u.c, l.a + r.a);
    u.d = max(u.d, l.a + r.b);
    u.e = max(u.e, l.b + r.a);
    u.f = max(u.f, l.b + r.b);
    u.g = max(u.g, l.a + r.f);
    u.g = max(u.g, l.d + r.b);
    u.h = max(u.h, l.a + r.d);
    u.h = max(u.h, l.c + r.b);
    u.i = max(u.i, l.a + r.e);
    u.i = max(u.i, l.d + r.a);
    u.j = max(u.j, l.b + r.d);
    u.j = max(u.j, l.e + r.b);
}

void pushup(int u)
{
    pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

void build(int u, int l, int r)
{
    tr[u].init(l, r);
    if(l == r) tr[u].a = w[l] , tr[u].b = -w[l];
    else
    {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}

Node query(int u, int l, int r)
{
    if(l <= tr[u].l && r >= tr[u].r) return tr[u];
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if(r <= mid) return query(u << 1, l, r);
        if(l > mid) return query(u << 1 | 1, l, r);
        Node res;
        res.init(0, 0);
        pushup(res, query(u << 1, l, r), query(u << 1 | 1, l, r));
        return res;
    }
}

void solve()
{
    cin >> n >> m;
    for(int i = 1 ; i <= n ; i ++ ) cin >> w[i] , w[i] = w[i] * w[i];
    build(1, 1, n);
    
    while (m -- )
    {
        int a, b;
        cin >> a >> b;
        Node t = query(1, a, b);
        cout << max(t.ans1, t.ans2) << endl;
    }
}

signed main()
{
    ios::sync_with_stdio(0),cin.tie(0);
    int T = 1;
    cin >> T;
    while(T -- ) solve();
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值