2019杭电多校第二场

2019杭电多校第二场

太菜了,被学弟暴打

1002. Beauty of Unimodal Sequence

upsolved

要求输出字典序最小和最大的最长单峰子序列

对于每一个位置,维护以这个位置结尾的前缀/后缀最长上升/单峰子序列长度,然后贪心输出(如果只要求长度正反求LIS就好了)

#include <bits/stdc++.h>
using namespace std;

const int N = 3e5 + 10;

int n, a[N];

struct Seg_Tree {
	int mx[N << 2];
	void init(int rt = 1, int l = 1, int r = n + 2) {
		mx[rt] = 0;
		if(l == r) return;
		int mid = l + r >> 1;
		init(rt << 1, l, mid);
		init(rt << 1 | 1, mid + 1, r);
	}
	void pushup(int rt) {
		mx[rt] = max(mx[rt << 1], mx[rt << 1 | 1]);
	}
	void update(int rt, int l, int r, int pos, int val) {
		if(l == r) {
			mx[rt] = max(mx[rt], val);
			return;
		}
		int mid = l + r >> 1;
		if(pos <= mid)
			update(rt << 1, l, mid, pos, val);
		else
			update(rt << 1 | 1, mid + 1, r, pos, val);
		pushup(rt);
	}
	int query(int rt, int l, int r, int L, int R) {
		if(L <= l && r <= R)
			return mx[rt];
		int mid = l + r >> 1, ans = 0;
		if(L <= mid)
			ans = max(ans, query(rt << 1, l, mid, L, R));
		if(R > mid)
			ans = max(ans, query(rt << 1 | 1, mid + 1, r, L, R));
		return ans;
	}
}zero, one;

int dp[2][N][2], len;
vector<int> ans[2], all, up[N], down[N];

void init() {
	len = 0;
	zero.init();
	one.init();
	all.clear();
	ans[0].clear();
	ans[1].clear();
	for(int i = 0; i <= n + 1; ++i)
		up[i].clear(), down[i].clear();
}

int main() {
	while(~scanf("%d", &n)) {
		init();
		for(int i = 1; i <= n; ++i) {
			scanf("%d", &a[i]);
			all.push_back(a[i]);
		}
		all.push_back(0);
		all.push_back(0x3f3f3f3f);
		sort(all.begin(), all.end());
		all.erase(unique(all.begin(), all.end()), all.end());
		for(int i = 1; i <= n; ++i) {
			a[i] = lower_bound(all.begin(), all.end(), a[i]) - all.begin() + 1;
		}
		for(int i = 1; i <= n; ++i) {
			dp[0][i][0] = zero.query(1, 1, n + 2, 1, a[i] - 1) + 1;
			dp[0][i][1] = max(one.query(1, 1, n + 2, a[i] + 1, n + 2) + 1, dp[0][i][0]);
			zero.update(1, 1, n + 2, a[i], dp[0][i][0]);
			one.update(1, 1, n + 2, a[i], dp[0][i][1]);
		}
		zero.init(); one.init();
		for(int i = n; i; --i) {
			dp[1][i][0] = zero.query(1, 1, n + 2, 1, a[i] - 1) + 1;
			dp[1][i][1] = max(one.query(1, 1, n + 2, a[i] + 1, n + 2) + 1, dp[1][i][0]);
			zero.update(1, 1, n + 2, a[i], dp[1][i][0]);
			one.update(1, 1, n + 2, a[i], dp[1][i][1]);
		}
		for(int i = 1; i <= n; ++i) {
			len = max(len, dp[0][i][0] + dp[1][i][1] - 1);
		}
		for(int i = 1; i <= n; ++i) {
			assert(len >= dp[0][i][1] + dp[1][i][0] - 1);
		}
		for(int i = 1; i <= n; ++i) {
			if(dp[0][i][0] + dp[1][i][1] - 1 == len) {
				up[dp[0][i][0]].push_back(i);
			}
			if(dp[0][i][1] + dp[1][i][0] - 1 == len) {
				down[dp[1][i][0]].push_back(i);
			}
		}
		int cur = 0, flag = 0;
		for(int i = 1; i <= len; ++i) {
			if(!flag) {
				vector<int> &v = up[i];
				int p = upper_bound(v.begin(), v.end(), cur) - v.begin();
				if(p == v.size() || a[v[p]] <= a[cur]) {
					flag = 1;
				}
				else {
					vector<int> &vv = down[len - i + 1];
					int pp = upper_bound(v.begin(), v.end(), cur) - v.begin();
					if(pp != vv.size() && a[vv[pp]] < a[cur] && vv[pp] < v[p]) {
						flag = 1;
					}
					else {
						cur = v[p];
						ans[0].push_back(v[p]);
					}
				}
			}
			if(flag) {
				vector<int> &v = down[len - i + 1];
				int p = upper_bound(v.begin(), v.end(), cur) - v.begin();
				assert(a[v[p]] < a[cur]);
				assert(p < v.size());
				cur = v[p];
				ans[0].push_back(v[p]);
			}
		}
		cur = flag = 0;
		for(int i = 1; i <= len; ++i) {
			if(!flag) {
				vector<int> &v = up[i];
				int p = lower_bound(v.begin(), v.end(), cur, [&](int i, int j){return a[i] > a[j];}) - v.begin() - 1;
				if(p < 0 || a[v[p]] <= a[cur]) {
					flag = 1;
				}
				else {
					cur = v[p];
					ans[1].push_back(v[p]);
				}
			}
			if(flag) {
				vector<int> &v = down[len - i + 1];
				int p = lower_bound(v.begin(), v.end(), cur, [&](int i, int j){return a[i] < a[j];}) - v.begin() - 1;
				assert(p >= 0);
				assert(a[v[p]] < a[cur]);
				cur = v[p];
				ans[1].push_back(v[p]);
			}
		}
		for(int _ = 0; _ < 2; ++_) {
			for(int i = 0; i < len; ++i)
				printf("%d%c", ans[_][i], " \n"[i + 1 == len]);
		}
	}
	return 0;
}

1005. Everything Is Generated In Equal Probability

solved at 01:45(+1)

求当前序列的逆序对数并且将当前序列随机替换成一个子序列(\(2^n\))种,递归处理每次结果相加直到序列为空为止

每次给定一个\({\rm N}\), 你随机取一个\(n\in[1,{\rm N}]\),然后随机取一个\(n\)个数的排列作为初始序列,求总逆序对数的期望

\(f(n)\)代表长为\(n\)的序列的期望

显然$$f(n) = \frac {n(n-1)} 4 + \sum\limits_{i = 0}^{n}\frac {C_n^i} {2^n}f(i)$$

\(n\)只有\(3000\),\(n^2\)递推即可,最后再求个前缀和

群里有老哥一眼看出答案是\(\frac {n^2-1} 9\) ....

1006. Fantastic Magic Cube

upsolved

题意转化之后就是你有一个\(n*n*n\)的立方体,每个\(1*1*1\)的小立方体都有自己的权值(权值为三个坐标的异或),你每次沿着一个方向把一个立方体切成两个立方体,获得的价值是两个立方体的权值和的乘积,你需要把它们都切成\(1*1*1\)的,求最大价值

听了dls题解,发现是个阅读理解,不管怎么切都是一样的

考虑两个不同的小立方体,它们的权值乘积一定被加到了最终的价值里,并且只加了一次

于是只要\(FWT\)求出每个权值的小立方体有多少个然后求一求和就好了

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int mod = 998244353, N = 1048576 + 10, g = 3;

LL qp(LL a, LL n) {
	LL res = 1;
	while(n) {
		if(n & 1) 
			res = res * a % mod;
		a = a * a % mod;
		n >>= 1;
	}
	return res;
}

const int inv2 = qp(2, mod - 2);

void FWT(int a[], int n) {  
    for(int d = 1; d < n; d <<= 1)  
        for(int m = d << 1, i = 0; i < n; i += m)  
            for(int j = 0; j < d; ++j) {  
                int x = a[i + j], y = a[i + j + d];  
                a[i + j] = (x + y) % mod, a[i + j + d] = (x - y + mod) % mod;   
            }  
}  

void IFWT(int a[], int n)   {  
    for(int d = 1; d < n; d <<= 1)  
        for(int m = d << 1, i = 0; i < n; i += m)  
            for(int j = 0; j < d; ++j) {  
                int x = a[i + j], y = a[i + j + d];  
                a[i + j] = 1LL * (x + y) * inv2 % mod, a[i + j + d] = (1LL * (x - y) * inv2 % mod + mod) % mod; 
            }  
} 

int n, a[N], m;
LL sum, ans;

int main() {
	while(~scanf("%d", &n)) {
		m = n;
		sum = ans = 0;
		for(; m & (m - 1); m += m & -m);
		for(int i = 0; i < m; ++i) 
			a[i] = 0;
		for(int i = 0; i < n; ++i)
			a[i] = 1;
		FWT(a, m);
		for(int i = 0; i < m; ++i)
			a[i] = 1LL * a[i] * a[i] % mod * a[i] % mod;
		IFWT(a, m);
		for(int i = 0; i < m; ++i)
			sum = (sum + 1LL * i * a[i]) % mod;
		for(int i = 0; i < m; ++i)
			ans = (ans + (1LL * i * a[i] % mod * (sum - i) % mod + mod) % mod) % mod;
		printf("%lld\n", ans * inv2 % mod);
	}
	return 0;
}

1009. I Love Palindrome String

待补, PAM待学

1010. Just Skip The Problem

solved at 00:31

直接输出\(n!\)就好了,\(1\)特判掉(不过我看有的人没特判就过了,怕不是没这数据。。。)

1011. Keen on Everything But Triangle

solved at 00:44

\(Q\)次询问一个区间里的数能构成的最大周长的三角形

因为数据范围是\(1e9\)所以大概四十几个数就一定能构成三角形,从大到小排序,先找\(123\),不行就\(234\),找个四十几次就行了

主席树实现的,线段树维护前\(45\)大暴力合并据说也可以

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int MAXN = 100010;
struct node {
	int l, r, sum;
}Tree[MAXN * 30];
int root[MAXN];

struct num {
	int x, id;
	bool operator < (const num &rhs) const {
		return x < rhs.x;
	}
}a[MAXN];

int rnk[MAXN];
int n, m, i, j, k, cnt, l, r, x, y, z;

void init() {
	cnt = 0;
	root[0] = 0;
	Tree[0].l = Tree[0].r = Tree[0].sum = 0;
}

void update(int &rt, int l, int r, int num) {
	Tree[++cnt] = Tree[rt];
	rt = cnt;
	Tree[rt].sum++;
	if(l == r)
		return;
	int mid = l + r >> 1;
	if(num <= mid)
		update(Tree[rt].l, l, mid, num);
	else
		update(Tree[rt].r, mid + 1, r, num);
}

int query(int i, int j, int k, int l, int r) {
	if(l == r)
		return l;
	int mid = l + r >> 1;
	int d = Tree[Tree[j].l].sum - Tree[Tree[i].l].sum;
	if(k <= d)
		return query(Tree[i].l, Tree[j].l, k, l, mid);
	else
		return query(Tree[i].r, Tree[j].r, k - d, mid + 1, r);
}

int main() {
	while(~scanf("%d %d", &n, &m)) {
		init();
		for(int i = 1; i <= n; ++i) {
			scanf("%d", &a[i].x);
			a[i].id = i;
		}
		sort(a + 1, a + n + 1);
		for(int i = 1; i <= n; ++i) {
			rnk[a[i].id] = i;
		}
		for(int i = 1; i <= n; ++i) {
			root[i] = root[i - 1];
			update(root[i], 1, n, rnk[i]);
		}
		while(m--) {
			scanf("%d %d", &l, &r);
			if(r - l < 2) {
				printf("-1\n");
				continue;
			}
			cnt = r - l + 1;
			x = a[query(root[l - 1], root[r], cnt, 1, n)].x;
			y = a[query(root[l - 1], root[r], cnt - 1, 1, n)].x;
			z = a[query(root[l - 1], root[r], cnt - 2, 1, n)].x;
			if(x < y + z) {
				printf("%lld\n", 0LL + x + y + z);
				continue;
			}
			bool flag = 0;
			for(int i = 3; i < cnt; ++i) {
				x = y;
				y = z;
				z = a[query(root[l - 1], root[r], cnt - i, 1, n)].x;
				if(x < y + z) {
					flag = 1;
					printf("%lld\n", 0LL + x + y + z);
					break;
				}
			}
			if(!flag) {
				printf("-1\n");
			}
		}
	}
	return 0;
}

1012. Longest Subarray

upsolved

自闭题

求最长的连续子序列满足序列里出现的数都至少出现了\(k\)

枚举右端点,线段树维护不合法的区间,每次询问最左边的合法位置即可

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;

int mn[N << 2], lazy[N << 2];

void pushdown(int rt) {
	if(lazy[rt]) {
		lazy[rt << 1] += lazy[rt];
		lazy[rt << 1 | 1] += lazy[rt];
		mn[rt << 1] += lazy[rt];
		mn[rt << 1 | 1] += lazy[rt];
		lazy[rt] = 0;
	}
}

void pushup(int rt) {
	mn[rt] = min(mn[rt << 1], mn[rt << 1 | 1]);
}

void build(int rt, int l, int r) {
	lazy[rt] = mn[rt] = 0;
	if(l == r)
		return;
	int mid = l + r >> 1;
	build(rt << 1, l, mid);
	build(rt << 1 | 1, mid + 1, r);
}

void update(int rt, int	l, int r, int L, int R, int val) {
	if(L <= l && r <= R) {
		mn[rt] += val;
		lazy[rt] += val;
		return;
	}
	pushdown(rt);
	int mid = l + r >> 1;
	if(L <= mid)
		update(rt << 1, l, mid, L, R, val);
	if(R > mid)
		update(rt << 1 | 1, mid + 1, r, L, R, val);
	pushup(rt);
}

int query(int rt, int l, int r) {
	if(mn[rt] > 0) return 0;
	if(l == r) return l;
	pushdown(rt);
	int mid = l + r >> 1;
	if(mn[rt << 1] == 0)
		return query(rt << 1, l, mid);
	return query(rt << 1 | 1, mid + 1, r);
}

int a[N], n, c, k, ans, i, j, x, y;
vector<int> pos[N];

int main() {
	while(~scanf("%d%d%d", &n, &c, &k)) {
		if(k == 1) {
			for(int i = 1; i <= n; ++i)
				scanf("%*d");
			printf("%d\n", n);
			continue;
		}
		build(1, 1, n);
		ans = 0;
		for(i = 1; i <= n; ++i) {
			scanf("%d", &a[i]);
			if(pos[a[i]].empty()) {
				update(1, 1, n, 1, i, 1);
				pos[a[i]].push_back(i);
			}
			else {
				x = pos[a[i]].size() - k;
				y = pos[a[i]].back();
				if(x < 0) {
					update(1, 1, n, 1, y, -1);
				}
				else {
					update(1, 1, n, pos[a[i]][x] + 1, y, -1);
				}
				pos[a[i]].push_back(i);
				x = pos[a[i]].size() - k;
				y = pos[a[i]].back();
				if(x < 0) {
					update(1, 1, n, 1, y, 1);
				}
				else {
					update(1, 1, n, pos[a[i]][x] + 1, y, 1);
				}
			}
			j = query(1, 1, n);
			if(j) ans = max(ans, i - j + 1);
		}
		for(int i = 1; i <= c; ++i)
			pos[i].clear();
		printf("%d\n", ans);
	}
	return 0;
}

1007dls发了ppt, 估计是学不会,1008队友补了,1009看什么时候有时间去学

posted @ 2019-07-25 21:53  图斯卡蓝瑟  阅读(394)  评论(0)    收藏  举报