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;
}