2020-08-30 联考
【Problem 1】
Statement:
Solution:
考虑在\(i\),\(i+1\)处使用操作,\(i\)号牌和第\(i+m\)牌会翻转。于是考虑新加一个操作,每次操作使得\(i,i+m\)号牌翻转。不断使用这个操作等价于不断使用偶数次原操作。于是可以把原操作替换成以下两个操作:
- 将\(1-m\)的牌翻转
- 将\(i\)和\(i+m\)号牌翻转
只用操作\(2\)可以将原序列划分成\(m\)组
- {\(1,m,2m,......\)}
- {\(2,m+1,2m+1,......\)}
- {\(3,m+2,2m+2,......\)}
......
使用时只有组内会互相影响,组与组之间不会影响,于是可以对每一组\(dp\),再把所有\(dp\)值相加。最后再用一次操作\(1\)调整奇偶性即可,时间复杂度为\(\mathcal O(Tn)\)。
Code
#include<bits/stdc++.h>
using namespace std;
namespace io {
const int SIZE = (1 << 21) + 1;
char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
// getchar
#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
// print the remaining part
inline void flush () {
fwrite (obuf, 1, oS - obuf, stdout);
oS = obuf;
}
// putchar
inline void putc (char x) {
*oS ++ = x;
if (oS == oT) flush ();
}
// input a signed integer
template <class I>
inline void gi (I &x) {
for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;
for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); x *= f;
}
// print a signed integer
template <class I>
inline void print (I x) {
if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;
while (x) qu[++ qr] = x % 10 + '0', x /= 10;
while (qr) putc (qu[qr --]);
}
//no need to call flush at the end manually!
struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
}
using io :: gi;
using io :: putc;
using io :: print;
const int MAXN = 1.1E6;
long long dp[2][2];
int V[MAXN];
int g[MAXN];
int A[MAXN];
int is[MAXN];
int n, t, m;
inline long long getAns(int lim)
{
for (int i = 0;i < 2; ++i)
dp[i][0] = dp[i][1] = -2e18;
dp[0][0] = 0;
int o = 1;
long long res = -2e18;
for (int i = 1;i < lim; ++i, o ^= 1)
{
for (int pre = 0;pre < 2; ++pre)
for (int now = 0;now < 2; ++now)
{
if (dp[o ^ 1][pre] == -2e18)
continue;
bool chose = (g[i] + pre + now) & 1;
if (chose)
dp[o][now] = max(dp[o ^ 1][pre] + V[i], dp[o][now]);
else
dp[o][now] = max(dp[o ^ 1][pre], dp[o][now]);
}
dp[o^1][0] = dp[o^1][1] = -2e18;
}
return max(dp[o ^ 1][0] + g[lim] * V[lim], dp[o ^ 1][1] + (g[lim] ^ 1) * V[lim]);;
}
inline void solve()
{
gi(n), gi(m);
long long ans = 0, tmp = 0;
for (int i = 1;i <= n; ++i)
gi(is[i]);
for (int i = 1;i <= n; ++i)
gi(A[i]);
for (int i = 1;i <= m; ++i)
if (i <= n)
{
int cnt = 0;
for (int j = i;j <= n; j += m)
V[++cnt] = A[j], g[cnt] = is[j];
ans += getAns(cnt);
}
if (m <= n)
{
for (int i = 1;i <= m; ++i)
is[i] ^= 1;
for (int i = 1;i <= m; ++i)
if (i <= n)
{
int cnt = 0;
for (int j = i;j <= n; j += m)
V[++cnt] = A[j], g[cnt] = is[j];
tmp += getAns(cnt);
}
}
cout << max(ans, tmp) << '\n';
}
int main(void)
{
gi(t);
while (t--)
solve();
return 0;
}
【Problem 2】
Statement:
Solution:
先考虑没有修改的情况,记\(f_x\)为在\(x\)处出发需要用技能的次数,\(limL_x\)表示从\(x\)往左第一个大于等于\(V_x\)的位置,\(limR_x\)同理。
那么有以下转移方程:
记\(maxVal_{i,j}\)表示\(\max\limits_{k=i}^jV_k\),\(A_x\)为\(maxVal_{1,x},maxVal_{2,x},...,maxVal_{x,x}\)的集合,\(B_x\)为\(maxVal_{x,x},maxVal_{x,x+1},...,maxVal_{x,n}\)的集合。
发现\(f_x=|A_x\bigcup B_x|-1\)
考虑一次交换对其他位置产生的影响,不妨设\(V_{x} < V_{x+1}\),对于\(i\in (limL_x,x)\cap N\)的位置,交换后会将\(V_x\)从\(B_i\)的删除。考虑\(A_i\)中是否存在\(V_x\),若\(V_{limL_x}=V_x\),那么这个区间的\(A_i\)都包含\(V_x\),答案不变,否则区间答案减一。右边同理,只要考虑什么情况答案会加1。对于\(V_x > V_{x+1}\)的情况反过来做即可。这些操作都可以用数据结构优化成单次\(\log_2n\),总时间复杂度为\(\mathcal O(q\log_2 n)\)
Code
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1.1E5;
int A[MAXN];
int ans[MAXN];
pair <int, int> B[MAXN];
int n, m;
class segTree
{
private:
int treeMax[MAXN<<2];
long long treeSum[MAXN<<2];
int treeTag[MAXN<<2];
inline void pushUp1(int o) { treeMax[o] = max(treeMax[o<<1], treeMax[o<<1|1]); }
inline void pushUp2(int o) { treeSum[o] = treeSum[o<<1] + treeSum[o<<1|1]; }
inline void pushDown(int o, int l, int r)
{
if (!treeTag[o])
return ;
treeTag[o<<1] += treeTag[o], treeTag[o<<1|1] += treeTag[o];
treeSum[o<<1] += 1ll * treeTag[o] * ((l + r >> 1) - l + 1), treeSum[o<<1|1] += 1ll * treeTag[o] * (r - (l + r >> 1));
treeTag[o] = 0;
}
public:
int a;
int b;
int c;
int d;
void buildTree(int o = 1, int l = 1, int r = n)
{
if (l == r)
return treeMax[o] = A[l], treeSum[o] = ans[l], void();
buildTree(o << 1 , l, l + r >> 1), buildTree(o << 1 | 1, (l + r >> 1) + 1, r), pushUp1(o), pushUp2(o);
}
int getLeftPos(int o = 1, int l = 1, int r = n)
{
if (a > b)
return 0;
if (a <= l && r <= b)
{
if (l == r)
return treeMax[o] >= c ? l : 0;
return (treeMax[o<<1|1] >= c) ? getLeftPos(o << 1 | 1, (l + r >> 1) + 1, r) : getLeftPos(o << 1 , l, l + r >> 1);
}
int res = 0;
if (b > (l + r >> 1))
{
res = getLeftPos(o << 1 | 1, (l + r >> 1) + 1, r);
if (res)
return res;
}
if (a <= (l + r >> 1))
return getLeftPos(o << 1 , l, l + r >> 1);
return res;
}
int getRightPos(int o = 1, int l = 1, int r = n)
{
if (a > b)
return 0;
if (a <= l && r <= b)
{
if (l == r)
return treeMax[o] >= c ? l : 0;
return (treeMax[o<<1] >= c) ? getRightPos(o << 1 , l, l + r >> 1) : getRightPos(o << 1 | 1, (l + r >> 1) + 1, r);
}
int res = 0;
if (a <= (l + r >> 1))
{
res = getRightPos(o << 1 , l, l + r >> 1);
if (res)
return res;
}
if (b > (l + r >> 1))
return getRightPos(o << 1 | 1, (l + r >> 1) + 1, r);
return res;
}
void modifySum(int o = 1, int l = 1, int r = n)
{
if (a > b)
return ;
if (a <= l && r <= b)
return treeTag[o] += d, treeSum[o] += d * (r - l + 1), void();
pushDown(o, l, r);
if (a <= (l + r >> 1))
modifySum(o << 1 , l, l + r >> 1);
if (b > (l + r >> 1))
modifySum(o << 1 | 1, (l + r >> 1) + 1, r);
pushUp2(o);
}
void changeMax(int o = 1, int l = 1, int r = n)
{
if (l == r)
return treeMax[o] = A[l], void();
if (a <= (l + r >> 1))
changeMax(o << 1 , l, l + r >> 1);
else
changeMax(o << 1 | 1, (l + r >> 1) + 1, r);
pushUp1(o);
}
void changeAns(int o = 1, int l = 1, int r = n)
{
if (l == r)
return treeSum[o] = ans[l], void();
pushDown(o, l, r);
if (a <= (l + r >> 1))
changeAns(o << 1 , l, l + r >> 1);
else
changeAns(o << 1 | 1, (l + r >> 1) + 1, r);
pushUp2(o);
}
int queryPosAns(int o = 1, int l = 1, int r = n)
{
if (l == r)
return treeSum[o];
pushDown(o, l, r);
if (a <= (l + r >> 1))
return queryPosAns(o << 1 , l, l + r >> 1);
return queryPosAns(o << 1 | 1, (l + r >> 1) + 1, r);
}
long long queryAllAns(int o = 1, int l = 1, int r = n)
{
if (a <= l && r <= b)
return treeSum[o];
pushDown(o, l, r);
long long res = 0;
if (a <= (l + r >> 1))
res = queryAllAns(o << 1 , l, l + r >> 1);
if (b > (l + r >> 1))
res += queryAllAns(o << 1 | 1, (l + r >> 1) + 1, r);
return res;
}
} T;
inline void init()
{
set <int> S;
for (int i = 1;i <= n; ++i)
B[i] = make_pair(-A[i], i);
sort(B + 1, B + 1 + n);
for (int i = 1;i <= n; ++i)
{
int pos = B[i].second;
set <int> :: iterator nowPos = S.insert(pos).first;
int pre = 0;
int nxt = 0;
if (nowPos != S.begin())
pre = *--nowPos, ++nowPos;
if ((++nowPos) != S.end())
nxt = *nowPos;
--nowPos;
if (!pre && !nxt)
ans[pos] = 0;
else
if (nxt && ((!pre) || (A[nxt] < A[pre])))
ans[pos] = ans[nxt] + (A[pos] < A[nxt]);
else
ans[pos] = ans[pre] + (A[pos] < A[pre]);
}
}
inline int calc(int pos)
{
T.a = 1, T.b = pos - 1, T.c = A[pos];
int limL = T.getLeftPos();
T.a = pos + 1, T.b = n;
int limR = T.getRightPos();
if (!limL && !limR)
return 0;
if ((limL) && ((!limR) || (A[limR] > A[limL])))
{
T.a = limL;
return T.queryPosAns() + (A[pos] < A[limL]);
}
T.a = limR;
return T.queryPosAns() + (A[pos] < A[limR]);
}
int main(void)
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
freopen("wizard.in", "r", stdin);
freopen("wizard.out", "w", stdout);
cin >> n >> m;
for (int i = 1;i <= n; ++i)
cin >> A[i];
init(), T.buildTree();
int opt = 0;
int x = 0;
while (cin >> opt)
{
if (opt == 1)
{
cin >> x;
if (A[x] == A[x + 1])
continue;
if (A[x] < A[x + 1])
{
T.a = 1, T.b = x - 1, T.c = A[x];
int limL = T.getLeftPos();
if ((T.a <= T.b) & ((!limL) | (A[limL] > A[x])))
T.a = limL + 1, T.b = x - 1, T.d = -1, T.modifySum();
T.a = x + 2, T.b = n, T.c = A[x];
int limR = T.getRightPos();
if (limR == 0)
limR = n + 1;
if ((T.a <= T.b) & ((limR == n + 1) | (A[limR] > A[x])))
T.a = x + 2, T.b = limR - 1, T.d = 1, T.modifySum();
swap(A[x], A[x + 1]);
}
else
{
T.a = 1, T.b = x - 1, T.c = A[x + 1];
int limL = T.getLeftPos();
if ((T.a <= T.b) & ((!limL) | (A[limL] > A[x + 1])))
T.a = limL + 1, T.b = x - 1, T.d = 1, T.modifySum();
T.a = x + 2, T.b = n, T.c = A[x + 1];
int limR = T.getRightPos();
if (limR == 0)
limR = n + 1;
if ((T.a <= T.b) & ((limR == n + 1) | (A[limR] > A[x + 1])))
T.a = x + 2, T.b = limR - 1, T.d = -1, T.modifySum();
swap(A[x], A[x + 1]);
}
int pos1 = x, pos2 = x + 1;
T.a = x, T.changeMax();
T.a = x + 1, T.changeMax();
if (A[pos1] < A[pos2])
swap(pos1, pos2);
ans[pos1] = calc(pos1), T.a = pos1, T.changeAns();
ans[pos2] = calc(pos2), T.a = pos2, T.changeAns();
}
else
{
cin >> T.a >> T.b;
cout << (1ll * T.queryAllAns() * m + 1ll * (T.b - T.a + 1) * (n - 1)) << '\n';
}
}
return 0;
}

浙公网安备 33010602011771号