JXOI2018题解

排列问题

比较明显的贪心吧,就先让排名最小的增加到排名第二的权值,再让排名第二的权值增加到排名第三的权值,sortsortsort完之后按顺序搞就好了,模数搞错弄了半天。。。


#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
const LL mod = 998244353;
const int P = 10200001;
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(int x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

int sta[200001];
int a[200001], b[200001];
int jc[P];

int pow_mod(int a, int k) {
	int ans = 1;
	while(k) {
		if(k & 1) ans = (LL)ans * a % mod;
		a = (LL)a * a % mod; k /= 2;
	} return ans;
}

int main() {
	int tt = read();
	jc[0] = 1; for(int i = 1; i < P; i++) jc[i] = (LL)jc[i - 1] * i % mod;
	while(tt--) {
		int n = read(), m = read(), L = read(), R = read();
		int gg = n + m;
		int u1 = 0, u2 = 0;
		for(int i = 1; i <= n; i++) {
			int x = read();
			if(x >= L && x <= R) a[++u1] = x;
			else b[++u2] = x;
		} sort(a + 1, a + u1 + 1), sort(b + 1, b + u2 + 1);
		int ans = 1; int l = 1;
		for(int i = 2; i <= u2; i++) {
			if(b[i] != b[i - 1]) {
				ans = (LL)ans * jc[i - l] % mod;
				l = i;
			}
		} ans = (LL)ans * jc[u2 - l + 1] % mod;
		int tp = 0, mx = 0; l = 1;
		for(int i = 2; i <= u1; i++) {
			if(a[i] != a[i - 1]) sta[++tp] = i - l, mx = _max(mx, i - l), l = i;
		} sta[++tp] = u1 - l + 1, mx = _max(mx, u1 - l + 1);
		sort(sta + 1, sta + tp + 1);
		LL s = 0;
		for(int i = 1; i <= tp; i++) s += mx - sta[i];
		s += (LL)mx * (R - L + 1 - tp);
		if(s <= m) {
			m -= s;
			int z = m % (R - L + 1), k = m / (R - L + 1);
			ans = (LL)ans * pow_mod(jc[mx + k + 1], z) % mod;
			ans = (LL)ans * pow_mod(jc[mx + k], R - L + 1 - z) % mod;
		} else {
			int lst = 0, o = R - L + 1 - tp;
			for(int i = 1; i <= tp; i++) {
				if(lst != sta[i]) {
					if(m < (LL)o * (sta[i] - lst)) break;
					m -= o * (sta[i] - lst);
					lst = sta[i];
				} o++;
			} if(o == 0) {
				for(int i = 1; i <= tp; i++) ans = (LL)ans * jc[sta[i]] % mod;
			} else {
				int z = m % o, k = m / o;
				ans = (LL)ans * pow_mod(jc[lst + k + 1], z) % mod;
				ans = (LL)ans * pow_mod(jc[lst + k], o - z) % mod;
				for(int i = o + 1 - (R - L + 1 - tp); i <= tp; i++) ans = (LL)ans * jc[sta[i]] % mod;
			}
		} put((LL)jc[gg] * pow_mod(ans, mod - 2) % mod), puts("");
	} return 0;
}

游戏

你只用考虑在[l,r][l,r][l,r]区间内不是[l,r][l,r][l,r]区间倍数的数,用线筛把这些数给筛出来,然后会有一个式子,假设这样的数有sss个i=0nsCnii!s!(nis)!Cns1s1(i+s)\sum_{i=0}^{n-s}C_n^ii!s!(n-i-s)!C_{n-s-1}^{s-1}(i+s)∑i=0n−s​Cni​i!s!(n−i−s)!Cn−s−1s−1​(i+s)你推一下就会变成(ns)!s(i+s1)!(i+s)/i!\sum(n-s)!s(i+s-1)!(i+s)/i!∑(n−s)!s(i+s−1)!(i+s)/i!,直接做就好了。


#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(int x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

int h[5001], f[5001][5001];

int main() {
	int n = read();
	for(int i = 1; i <= n; i++) h[i] = read();
	int ans = 0;
	for(int r = 1; r <= n; r++) {
		f[r][r] = f[r - 1][r] = 1;
		int Y = h[r] - h[r - 1], X = 1, now;
		bool bk = 0;
		for(int l = r - 2; l >= 1; l--) {
			int x = r - l, y = h[r] - h[l];
			if((LL)Y * x > (LL)y * X) f[l][r] = f[l + 1][r], X = x, Y = y, bk = 0;
			else {
				if(!bk) bk = 1, f[l][r] = f[l + 1][r] + 1, now = l;
				else f[l][r] = _min(f[l][now] + f[now + 1][r], f[l][now + 1] + f[now + 2][r]);
			}
		}
	} for(int l = 1; l <= n; l++) for(int r = l; r <= n; r++) ans ^= f[l][r];
	put(ans);
	return 0;
}


守卫

emm。。。
首先一个点rrr可以看到另一个点lll,且l&lt;rl&lt;rl<r,当且仅当其中的点与rrr的斜率都大于lll与rrr之间的斜率。
然后你考虑对于一段区间[l,r][l,r][l,r],rrr肯定要放,类似的假设有一段rrr看不到的区间[x,y][x,y][x,y]则要么yyy放,要么y+1y+1y+1放,直接dpdpdp转移即可。


#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(int x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

int h[5001], f[5001][5001];

int main() {
	int n = read();
	for(int i = 1; i <= n; i++) h[i] = read();
	int ans = 0;
	for(int r = 1; r <= n; r++) {
		f[r][r] = f[r - 1][r] = 1;
		int Y = h[r] - h[r - 1], X = 1, now;
		bool bk = 0;
		for(int l = r - 2; l >= 1; l--) {
			int x = r - l, y = h[r] - h[l];
			if((LL)Y * x > (LL)y * X) f[l][r] = f[l + 1][r], X = x, Y = y, bk = 0;
			else {
				if(!bk) bk = 1, f[l][r] = f[l + 1][r] + 1, now = l;
				else f[l][r] = _min(f[l][now] + f[now + 1][r], f[l][now + 1] + f[now + 2][r]);
			}
		}
	} for(int l = 1; l <= n; l++) for(int r = l; r <= n; r++) ans ^= f[l][r];
	put(ans);
	return 0;
}

上一篇:JXOI2018做题笔记


下一篇:ES10特性详解