2020-08-30 联考

【Problem 1】

Statement:

problem

Solution:

考虑在\(i\),\(i+1\)处使用操作,\(i\)号牌和第\(i+m\)牌会翻转。于是考虑新加一个操作,每次操作使得\(i,i+m\)号牌翻转。不断使用这个操作等价于不断使用偶数次原操作。于是可以把原操作替换成以下两个操作:

  1. \(1-m\)的牌翻转
  2. \(i\)\(i+m\)号牌翻转

只用操作\(2\)可以将原序列划分成\(m\)

  1. {\(1,m,2m,......\)}
  2. {\(2,m+1,2m+1,......\)}
  3. {\(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:

problem

Solution:

先考虑没有修改的情况,记\(f_x\)为在\(x\)处出发需要用技能的次数,\(limL_x\)表示从\(x\)往左第一个大于等于\(V_x\)的位置,\(limR_x\)同理。
那么有以下转移方程:

\[f_x\gets f_{limL_x}+(V_x<V_{limL_x}) | (x=n)或V_{limL_x}\leq V_{limR_x} \]

\[f_x\gets f_{limR_x}+(V_x<V_{limR_x}) | (x=1)或V_{limR_x}< V_{limL_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;
}
posted @ 2020-08-30 20:21  Beginner2670  阅读(111)  评论(0)    收藏  举报