【CodeForces】CodeForces Round #512 (Div. 1) 题解

【比赛链接】

【题解链接】

**【A】**Vasya and Triangle

【思路要点】

  • 任何三格点角形的面积均是 0.5 0.5 0.5 的整数倍,因此当 2 ∗ N ∗ M 2*N*M 2NM 不是 k k k 的倍数,问题无解。
  • 否则,令 g = g c d ( N , k ) , t n p = N g , t m p = M ∗ g k g=gcd(N,k),tnp=\frac{N}{g},tmp=\frac{M*g}{k} g=gcd(N,k),tnp=gN,tmp=kMg
  • 选取 ( 0 , 0 ) , ( 2 ∗ t n p , 0 ) , ( 0 , t m p ) (0,0),(2*tnp,0),(0,tmp) (0,0),(2tnp,0),(0,tmp) ( 0 , 0 ) , ( t n p , 0 ) , ( 0 , 2 ∗ t m p ) (0,0),(tnp,0),(0,2*tmp) (0,0),(tnp,0),(0,2tmp) 中合法的一个作为答案。
  • 时间复杂度 O ( L o g V ) O(LogV) O(LogV)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
ll n, m, k;
ll gcd(ll a, ll b) {
	if (b == 0) return a;
	else return gcd(b, a % b);
}
int main() {
	read(n), read(m), read(k);
	ll g = gcd(n, k);
	k = k / g;
	if (m * 2 % k != 0) printf("NO\n");
	else {
		ll x = n / g, y = m * 2 / k;
		if (y > m) x *= 2, y /= 2;
		printf("YES\n");
		printf("0 0\n");
		cout << x << ' '<< 0 << endl;
		cout << 0 << ' ' << y << endl;
	}
}

**【B】**Vasya and Good Sequences

【思路要点】

  • A i A_i Ai 表示 a i a_i ai 二进制表示中 1 1 1 的个数。
  • 一个序列 [ l , r ] [l,r] [l,r] 合法当且仅当 M a x i = l r { A i } ≤ ∑ i = l r A i 2 Max_{i=l}^{r}\{A_i\}≤\frac{\sum_{i=l}^{r}A_i}{2} Maxi=lr{Ai}2i=lrAi ∑ i = l r A i \sum_{i=l}^{r}A_i i=lrAi 2 2 2 的倍数。
  • 注意到 M a x i = l r { A i } ≤ 64 Max_{i=l}^{r}\{A_i\}≤64 Maxi=lr{Ai}64 ,枚举左端点 l l l ,枚举满足 r ≤ l + 128 r≤l+128 rl+128 的右端点,用后缀和处理剩余的 r r r
  • 时间复杂度 O ( N L o g A i ) O(NLogA_i) O(NLogAi)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int a[MAXN], s[MAXN][2], sum[MAXN], nxt[MAXN];
int main() {
	int n; read(n);
	for (int i = 1; i <= n; i++) {
		ll tmp; read(tmp);
		while (tmp) {
			a[i] += tmp & 1;
			tmp >>= 1;
		}
	}
	for (int i = 1; i <= n; i++){
		sum[i] = sum[i - 1] + a[i];
		s[i][1] = s[i - 1][1];
		s[i][0] = s[i - 1][0];
		if (sum[i] % 2 == 1) s[i][1]++;
		else s[i][0]++;
	}
	int las = n + 1;
	for (int i = n; i >= 0; i--){
		nxt[i] = las;
		if (a[i] != 0) las = i;
	}
	ll ans = 0;
	for (int i = 1; i <= n; i++){
		int tmp = sum[i - 1] % 2;
		int las = i, Max = a[i], sum = a[i];
		for (int j = 1; j <= 128; j++){
			int id = nxt[las]; 
			if (sum / 2 >= Max){
				ans = ans + s[id - 1][tmp] - s[las - 1][tmp];
			}
			Max = max(a[id], Max);
			sum = sum + a[id];
			las = id;
			if (las > n) break;
		}
		ans = ans + s[n][tmp] - s[las - 1][tmp];
	}
	writeln(ans);
	return 0;
}

**【C】**Putting Boxes Together

【思路要点】

  • 每次移动必然会有一个物品不动。
  • 这个物品一定是处在权值 1 2 \frac{1}{2} 21 处的一个物品(或两个物品中的任意一个),通过线段树上二分计算得到该点。
  • 用线段树维护区间权值和、权值乘下标和、权值乘位置和,计算询问的答案即可。
  • 时间复杂度 O ( N + Q L o g N ) O(N+QLogN) O(N+QLogN)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int P = 1e9 + 7;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct SegmentTree {
	struct Node {
		int lc, rc;
		long long sum, prd, num;
	} a[MAXN * 2];
	int n, size, root;
	int pos[MAXN], val[MAXN];
	void update(int root) {
		a[root].sum = a[a[root].lc].sum + a[a[root].rc].sum;
		a[root].prd = (a[a[root].lc].prd + a[a[root].rc].prd) % P;
		a[root].num = (a[a[root].lc].num + a[a[root].rc].num) % P;
	}
	void build(int &root, int l, int r) {
		root = ++size;
		if (l == r) {
			a[root].sum = val[l];
			a[root].num = 1ll * val[l] * l % P;
			a[root].prd = 1ll * val[l] * pos[l] % P;
			return;
		}
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid);
		build(a[root].rc, mid + 1, r);
		update(root);
	}
	void init(int x) {
		n = x;
		root = size = 0;
		for (int i = 1; i <= n; i++)
			read(pos[i]);
		for (int i = 1; i <= n; i++)
			read(val[i]);
		build(root, 1, n);
	}
	void modify(int root, int l, int r, int p) {
		if (l == r) {
			a[root].sum = val[l];
			a[root].num = 1ll * val[l] * l % P;
			a[root].prd = 1ll * val[l] * pos[l] % P;
			return;
		}
		int mid = (l + r) / 2;
		if (mid >= p) modify(a[root].lc, l, mid, p);
		else modify(a[root].rc, mid + 1, r, p);
		update(root);
	}
	void modify(int pos, int d) {
		val[pos] = d;
		modify(root, 1, n, pos);
	}
	ll querys(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) return a[root].sum;
		long long ans = 0;
		int mid = (l + r) / 2;
		if (mid >= ql) ans += querys(a[root].lc, l, mid, ql, min(mid, qr));
		if (mid + 1 <= qr) ans += querys(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);
		return ans;
	}
	ll querys(int l, int r) {
		if (l > r) return 0; 
		else return querys(root, 1, n, l, r);
	}
	ll queryp(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) return a[root].prd;
		long long ans = 0;
		int mid = (l + r) / 2;
		if (mid >= ql) ans += queryp(a[root].lc, l, mid, ql, min(mid, qr));
		if (mid + 1 <= qr) ans += queryp(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);
		return ans % P;
	}
	ll queryp(int l, int r) {
		if (l > r) return 0; 
		else return queryp(root, 1, n, l, r);
	}
	ll queryn(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) return a[root].num;
		long long ans = 0;
		int mid = (l + r) / 2;
		if (mid >= ql) ans += queryn(a[root].lc, l, mid, ql, min(mid, qr));
		if (mid + 1 <= qr) ans += queryn(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);
		return ans % P;
	}
	ll queryn(int l, int r) {
		if (l > r) return 0; 
		else return queryn(root, 1, n, l, r);
	}
	int findmid(int root, int l, int r, ll pos) {
		if (l == r) return l;
		int mid = (l + r) / 2;
		if (pos <= a[a[root].lc].sum) return findmid(a[root].lc, l, mid, pos);
		else return findmid(a[root].rc, mid + 1, r, pos - a[a[root].lc].sum);
	}
	ll mquery(int l, int r) {
		ll tmp = querys(l, r);
		ll tnp = querys(1, l - 1) + (tmp + 1) / 2;
		int mid = findmid(root, 1, n, tnp);
		ll ls = querys(l, mid) % P, rs = querys(mid, r) % P;
		ll ans = ls * pos[mid] % P - queryp(l, mid);
		ans += queryp(mid, r) - rs * pos[mid] % P;
		ans -= 1ll * mid * ls % P - queryn(l, mid);
		ans -= queryn(mid, r) - 1ll * mid * rs % P;
		ans = (ans % P + P) % P;
		return ans;
	}
} ST;
int main() {
	int n, m; read(n), read(m);
	ST.init(n);
	for (int i = 1; i <= m; i++) {
		int x, y; read(x), read(y);
		if (x <= 0) ST.modify(-x, y);
		else writeln(ST.mquery(x, y));
	}
	return 0;
}

**【D】**Linear Congruential Generator

【思路要点】

  • 质数 p p p 一定存在原根。
  • 线性数列 x i = a ∗ x i − 1 + b   ( a ≠ 0 , 1 ) x_i=a*x_{i-1}+b\ (a\ne 0,1) xi=axi1+b (a̸=0,1) 存在通项 x i = a i ∗ c + d x_i=a^i*c+d xi=aic+d ,因此,当 a a a p p p 的原根时,数列的周期为 p − 1 p-1 p1 ,否则,数列的周期为 p − 1 p-1 p1 某个因数。
  • a = 1 a=1 a=1 b ≠ 0 b\ne 0 b̸=0 ,数列的周期为 p p p
  • a = 1 a=1 a=1 b = 0 b=0 b=0 ,数列的周期为 1 1 1
  • a = 0 a=0 a=0 b = x 0 b=x_0 b=x0 ,数列的周期为 1 1 1
  • a = 0 a=0 a=0 b ≠ x 0 b\ne x_0 b̸=x0 ,数列的周期为 1 1 1 ,且进入周期之前有 1 1 1 个不在周期内的元素。
  • 能够被生成的多元组的个数应当为各个数列中:进入周期之前不在周期内的元素个数的最大值 + + + 周期长度的最小公倍数。
  • 若不考虑 a = 0 a=0 a=0 b ≠ x 0 b\ne x_0 b̸=x0 的数列,我们应当只会选择周期为 p p p p − 1 p-1 p1 的数列,显然有一种从大到小贪心的做法,即优先选取 p p p ,若当前 L C M LCM LCM 中以及有 p p p 因子,则选取 p − 1 p-1 p1
  • 考虑 a = 0 a=0 a=0 b ≠ x 0 b\ne x_0 b̸=x0 的数列,在上述贪心中,若存在一个数列,去掉它, L C M LCM LCM 保持不变,则答案应当 + 1 +1 +1 。可以证明,上述贪心中被当做周期为 p p p 的数列依然只需要被当做周期为 p p p 的数列考虑,周期为 p − 1 p-1 p1 的数列依然只需要被当做周期为 p − 1 p-1 p1 的数列考虑。
  • 时间复杂度 O ( N L o g V + V ) O(NLogV+V) O(NLogV+V)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXV = 2e6 + 5;
const int P = 1e9 + 7;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n, tot, prime[MAXV], f[MAXV];
int a[MAXN], Max[MAXV], cnt[MAXV];
bool type[MAXN];
void init(int n) {
	for (int i = 2; i <= n; i++) {
		if (f[i] == 0) prime[++tot] = f[i] = i;
		for (int j = 1; j <= tot && prime[j] <= f[i]; j++) {
			int tmp = prime[j] * i;
			if (tmp > n) break;
			f[tmp] = prime[j];
		}
	}
}
int main() {
	read(n);
	for (int i = 1; i <= n; i++)
		read(a[i]);
	init(2e6);
	sort(a + 1, a + n + 1);
	reverse(a + 1, a + n + 1);
	for (int i = 1; i <= n; i++) {
		if (Max[a[i]]) {
			type[i] = true;
			int tmp = a[i] - 1;
			while (tmp != 1) {
				int fac = f[tmp], now = 0;
				while (tmp % fac == 0) {
					tmp /= fac;
					now++;
				}
				if (now > Max[fac]) {
					Max[fac] = now;
					cnt[fac] = 1;
				} else cnt[fac] += Max[fac] == now;
			}
		} else Max[a[i]] = cnt[a[i]] = 1;
	}
	int ans = 1;
	for (int i = 1; i <= 2e6; i++)
	for (int j = 1; j <= Max[i]; j++)
		ans = 1ll * ans * i % P;
	for (int i = 1; i <= n; i++) {
		bool found = false;
		if (type[i]) {
			found = true;
			int tmp = a[i] - 1;
			while (tmp != 1) {
				int fac = f[tmp], now = 0;
				while (tmp % fac == 0) {
					tmp /= fac;
					now++;
				}
				found &= Max[fac] > now || cnt[fac] >= 2;
			}
		} else found = Max[a[i]] >= 2 || cnt[a[i]] >= 2;
		if (found) {
			writeln((ans + 1) % P);
			return 0;
		}
	}
	writeln(ans);
	return 0;
}

**【E】**Euler tour

【思路要点】

  • 一个合法的欧拉序应当满足 a 1 = a 2 n − 1 a_1=a_{2n-1} a1=a2n1 ,不存在 i &lt; j &lt; k &lt; l i&lt;j&lt;k&lt;l i<j<k<l 使得 a i = a k , a j = a l , a i ≠ a j a_i=a_k,a_j=a_l,a_i\ne a_j ai=ak,aj=al,ai̸=aj ,并且若 i &lt; j , a i = a j i&lt;j,a_i=a_j i<j,ai=aj ,那么 j − i j-i ji 是偶数。
  • 对于满足上述几点的输入,我们可以用下面的算法构造出一组解。
  • 1 1 1 、若 a i = a j a_i=a_j ai=aj ,递归处理区间 ( i , j ) (i,j) (i,j) ,然后将区间 [ i , j ] [i,j] [i,j] 缩成一个点。
  • 2 2 2 、若不出现重复数的区间 ( i , j ) (i,j) (i,j) 元素个数为 2 k − 1 2k-1 2k1 ,其中非零元素个数应当不超过 k k k ,否则问题无解。将非零元素个数补足 k k k 个,则有以下两种情况:
    ( 1 ) (1) (1) 、存在三个连续的位置 0 , i , j 0,i,j 0,i,j ,将 0 0 0 赋为 j j j ,然后将这三个数看做一个 j j j
    ( 2 ) (2) (2) 、存在三个连续的位置 i , j , 0 i,j,0 i,j,0 ,将 0 0 0 赋为 i i i ,然后将这三个数看做一个 i i i
    ( 3 ) (3) (3) 、不存在上述连续位置,则序列形如 i , 0 , j , 0 , k , . . . , 0 , l i,0,j,0,k,...,0,l i,0,j,0,k,...,0,l ,在所有序列中的 0 0 0 处填入该子树的根部即可。
  • 时间复杂度 O ( N ) O(N) O(N)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int a[MAXN], nxtoccur[MAXN];
int n, pre[MAXN], suf[MAXN];
int now, vis[MAXN];
void error() {
	printf("no\n");
	exit(0);
}
int getnum() {
	while (vis[now]) now++;
	if (now > n) error();
	vis[now] = 1;
	return now;
}
void solve(int l, int r) {
	if ((r - l) % 2 != 0) error();
	int start = pre[l], endpos = suf[r];
	for (int i = suf[start]; i <= r; i = nxtoccur[i] ? nxtoccur[i] : suf[i]) {
		if (nxtoccur[i]) {
			int tmp = nxtoccur[i];
			if (tmp > r) error();
			solve(suf[i], pre[tmp]);
			int tnp = pre[i];
			suf[tnp] = tmp, pre[tmp] = tnp;
		}
	}
	int cnt = 0, all = 0;
	for (int i = suf[start]; i <= r; i = suf[i])
		all++, cnt += a[i] != 0;
	int goal = (all + 1) / 2;
	if (cnt > goal) error();
	for (int i = suf[suf[start]]; i <= r && cnt < goal; i = suf[i]) {
		if (a[i] == 0) {
			cnt++;
			a[i] = getnum();
		}
	}
	if (all != 1) {
		for (int i = suf[start], j = suf[suf[start]]; j <= r; i = suf[i], j = suf[j]) {
			while (i > suf[start] && a[i] && a[j]) {
				int tmp = pre[i];
				if (a[tmp] == 0) {
					a[tmp] = a[j];
					int tnp = pre[tmp];
					suf[tnp] = j, pre[j] = tnp;
					i = tnp;
				} else break;
			}
			while (j < r && a[i] && a[j]) {
				int tmp = suf[j];
				if (a[tmp] == 0) {
					a[tmp] = a[i];
					int tnp = suf[tmp];
					suf[i] = tnp, pre[tnp] = i;
					if (pre[i] >= l) j = i, i = pre[i];
					else j = tnp;
				} else break;
			}
		}
		for (int i = suf[start]; i <= r; i = suf[i])
			if (a[i] == 0) a[i] = a[start];
	} else if (cnt == 0) a[suf[start]] = getnum();
	endpos = pre[endpos];
	suf[start] = endpos;
	pre[endpos] = start;
}
int main() {
	read(n);
	for (int i = 1; i <= 2 * n - 1; i++) {
		read(a[i]);
		suf[i] = i + 1;
		pre[i] = i - 1;
	}
	if (a[1] && a[2 * n - 1] && a[1] != a[2 * n - 1]) error();
	if (a[1] == 0 || a[2 * n - 1] == 0) a[1] = a[2 * n - 1] = a[1] + a[2 * n - 1];
	vis[0] = 1;
	for (int i = 2 * n - 1; i >= 1; i--) {
		if (a[i]) {
			if (vis[a[i]]) nxtoccur[i] = vis[a[i]];
			vis[a[i]] = i;
		}
	}
	suf[0] = 1, pre[2 * n] = 2 * n - 1;
	solve(1, 2 * n - 1);
	printf("yes\n");
	for (int i = 1; i <= 2 * n - 1; i++)
		printf("%d ", a[i]);
	return 0;
}
```**【比赛链接】**

【题解链接】

**【A】**Vasya and Triangle

【思路要点】

  • 任何三格点角形的面积均是 0.5 0.5 0.5 的整数倍,因此当 2 ∗ N ∗ M 2*N*M 2NM 不是 k k k 的倍数,问题无解。
  • 否则,令 g = g c d ( N , k ) , t n p = N g , t m p = M ∗ g k g=gcd(N,k),tnp=\frac{N}{g},tmp=\frac{M*g}{k} g=gcd(N,k),tnp=gN,tmp=kMg
  • 选取 ( 0 , 0 ) , ( 2 ∗ t n p , 0 ) , ( 0 , t m p ) (0,0),(2*tnp,0),(0,tmp) (0,0),(2tnp,0),(0,tmp) ( 0 , 0 ) , ( t n p , 0 ) , ( 0 , 2 ∗ t m p ) (0,0),(tnp,0),(0,2*tmp) (0,0),(tnp,0),(0,2tmp) 中合法的一个作为答案。
  • 时间复杂度 O ( L o g V ) O(LogV) O(LogV)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
ll n, m, k;
ll gcd(ll a, ll b) {
	if (b == 0) return a;
	else return gcd(b, a % b);
}
int main() {
	read(n), read(m), read(k);
	ll g = gcd(n, k);
	k = k / g;
	if (m * 2 % k != 0) printf("NO\n");
	else {
		ll x = n / g, y = m * 2 / k;
		if (y > m) x *= 2, y /= 2;
		printf("YES\n");
		printf("0 0\n");
		cout << x << ' '<< 0 << endl;
		cout << 0 << ' ' << y << endl;
	}
}

**【B】**Vasya and Good Sequences

【思路要点】

  • A i A_i Ai 表示 a i a_i ai 二进制表示中 1 1 1 的个数。
  • 一个序列 [ l , r ] [l,r] [l,r] 合法当且仅当 M a x i = l r { A i } ≤ ∑ i = l r A i 2 Max_{i=l}^{r}\{A_i\}≤\frac{\sum_{i=l}^{r}A_i}{2} Maxi=lr{Ai}2i=lrAi ∑ i = l r A i \sum_{i=l}^{r}A_i i=lrAi 2 2 2 的倍数。
  • 注意到 M a x i = l r { A i } ≤ 64 Max_{i=l}^{r}\{A_i\}≤64 Maxi=lr{Ai}64 ,枚举左端点 l l l ,枚举满足 r ≤ l + 128 r≤l+128 rl+128 的右端点,用后缀和处理剩余的 r r r
  • 时间复杂度 O ( N L o g A i ) O(NLogA_i) O(NLogAi)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int a[MAXN], s[MAXN][2], sum[MAXN], nxt[MAXN];
int main() {
	int n; read(n);
	for (int i = 1; i <= n; i++) {
		ll tmp; read(tmp);
		while (tmp) {
			a[i] += tmp & 1;
			tmp >>= 1;
		}
	}
	for (int i = 1; i <= n; i++){
		sum[i] = sum[i - 1] + a[i];
		s[i][1] = s[i - 1][1];
		s[i][0] = s[i - 1][0];
		if (sum[i] % 2 == 1) s[i][1]++;
		else s[i][0]++;
	}
	int las = n + 1;
	for (int i = n; i >= 0; i--){
		nxt[i] = las;
		if (a[i] != 0) las = i;
	}
	ll ans = 0;
	for (int i = 1; i <= n; i++){
		int tmp = sum[i - 1] % 2;
		int las = i, Max = a[i], sum = a[i];
		for (int j = 1; j <= 128; j++){
			int id = nxt[las]; 
			if (sum / 2 >= Max){
				ans = ans + s[id - 1][tmp] - s[las - 1][tmp];
			}
			Max = max(a[id], Max);
			sum = sum + a[id];
			las = id;
			if (las > n) break;
		}
		ans = ans + s[n][tmp] - s[las - 1][tmp];
	}
	writeln(ans);
	return 0;
}

**【C】**Putting Boxes Together

【思路要点】

  • 每次移动必然会有一个物品不动。
  • 这个物品一定是处在权值 1 2 \frac{1}{2} 21 处的一个物品(或两个物品中的任意一个),通过线段树上二分计算得到该点。
  • 用线段树维护区间权值和、权值乘下标和、权值乘位置和,计算询问的答案即可。
  • 时间复杂度 O ( N + Q L o g N ) O(N+QLogN) O(N+QLogN)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int P = 1e9 + 7;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct SegmentTree {
	struct Node {
		int lc, rc;
		long long sum, prd, num;
	} a[MAXN * 2];
	int n, size, root;
	int pos[MAXN], val[MAXN];
	void update(int root) {
		a[root].sum = a[a[root].lc].sum + a[a[root].rc].sum;
		a[root].prd = (a[a[root].lc].prd + a[a[root].rc].prd) % P;
		a[root].num = (a[a[root].lc].num + a[a[root].rc].num) % P;
	}
	void build(int &root, int l, int r) {
		root = ++size;
		if (l == r) {
			a[root].sum = val[l];
			a[root].num = 1ll * val[l] * l % P;
			a[root].prd = 1ll * val[l] * pos[l] % P;
			return;
		}
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid);
		build(a[root].rc, mid + 1, r);
		update(root);
	}
	void init(int x) {
		n = x;
		root = size = 0;
		for (int i = 1; i <= n; i++)
			read(pos[i]);
		for (int i = 1; i <= n; i++)
			read(val[i]);
		build(root, 1, n);
	}
	void modify(int root, int l, int r, int p) {
		if (l == r) {
			a[root].sum = val[l];
			a[root].num = 1ll * val[l] * l % P;
			a[root].prd = 1ll * val[l] * pos[l] % P;
			return;
		}
		int mid = (l + r) / 2;
		if (mid >= p) modify(a[root].lc, l, mid, p);
		else modify(a[root].rc, mid + 1, r, p);
		update(root);
	}
	void modify(int pos, int d) {
		val[pos] = d;
		modify(root, 1, n, pos);
	}
	ll querys(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) return a[root].sum;
		long long ans = 0;
		int mid = (l + r) / 2;
		if (mid >= ql) ans += querys(a[root].lc, l, mid, ql, min(mid, qr));
		if (mid + 1 <= qr) ans += querys(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);
		return ans;
	}
	ll querys(int l, int r) {
		if (l > r) return 0; 
		else return querys(root, 1, n, l, r);
	}
	ll queryp(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) return a[root].prd;
		long long ans = 0;
		int mid = (l + r) / 2;
		if (mid >= ql) ans += queryp(a[root].lc, l, mid, ql, min(mid, qr));
		if (mid + 1 <= qr) ans += queryp(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);
		return ans % P;
	}
	ll queryp(int l, int r) {
		if (l > r) return 0; 
		else return queryp(root, 1, n, l, r);
	}
	ll queryn(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) return a[root].num;
		long long ans = 0;
		int mid = (l + r) / 2;
		if (mid >= ql) ans += queryn(a[root].lc, l, mid, ql, min(mid, qr));
		if (mid + 1 <= qr) ans += queryn(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);
		return ans % P;
	}
	ll queryn(int l, int r) {
		if (l > r) return 0; 
		else return queryn(root, 1, n, l, r);
	}
	int findmid(int root, int l, int r, ll pos) {
		if (l == r) return l;
		int mid = (l + r) / 2;
		if (pos <= a[a[root].lc].sum) return findmid(a[root].lc, l, mid, pos);
		else return findmid(a[root].rc, mid + 1, r, pos - a[a[root].lc].sum);
	}
	ll mquery(int l, int r) {
		ll tmp = querys(l, r);
		ll tnp = querys(1, l - 1) + (tmp + 1) / 2;
		int mid = findmid(root, 1, n, tnp);
		ll ls = querys(l, mid) % P, rs = querys(mid, r) % P;
		ll ans = ls * pos[mid] % P - queryp(l, mid);
		ans += queryp(mid, r) - rs * pos[mid] % P;
		ans -= 1ll * mid * ls % P - queryn(l, mid);
		ans -= queryn(mid, r) - 1ll * mid * rs % P;
		ans = (ans % P + P) % P;
		return ans;
	}
} ST;
int main() {
	int n, m; read(n), read(m);
	ST.init(n);
	for (int i = 1; i <= m; i++) {
		int x, y; read(x), read(y);
		if (x <= 0) ST.modify(-x, y);
		else writeln(ST.mquery(x, y));
	}
	return 0;
}

**【D】**Linear Congruential Generator

【思路要点】

  • 质数 p p p 一定存在原根。
  • 线性数列 x i = a ∗ x i − 1 + b   ( a ≠ 0 , 1 ) x_i=a*x_{i-1}+b\ (a\ne 0,1) xi=axi1+b (a̸=0,1) 存在通项 x i = a i ∗ c + d x_i=a^i*c+d xi=aic+d ,因此,当 a a a p p p 的原根时,数列的周期为 p − 1 p-1 p1 ,否则,数列的周期为 p − 1 p-1 p1 某个因数。
  • a = 1 a=1 a=1 b ≠ 0 b\ne 0 b̸=0 ,数列的周期为 p p p
  • a = 1 a=1 a=1 b = 0 b=0 b=0 ,数列的周期为 1 1 1
  • a = 0 a=0 a=0 b = x 0 b=x_0 b=x0 ,数列的周期为 1 1 1
  • a = 0 a=0 a=0 b ≠ x 0 b\ne x_0 b̸=x0 ,数列的周期为 1 1 1 ,且进入周期之前有 1 1 1 个不在周期内的元素。
  • 能够被生成的多元组的个数应当为各个数列中:进入周期之前不在周期内的元素个数的最大值 + + + 周期长度的最小公倍数。
  • 若不考虑 a = 0 a=0 a=0 b ≠ x 0 b\ne x_0 b̸=x0 的数列,我们应当只会选择周期为 p p p p − 1 p-1 p1 的数列,显然有一种从大到小贪心的做法,即优先选取 p p p ,若当前 L C M LCM LCM 中以及有 p p p 因子,则选取 p − 1 p-1 p1
  • 考虑 a = 0 a=0 a=0 b ≠ x 0 b\ne x_0 b̸=x0 的数列,在上述贪心中,若存在一个数列,去掉它, L C M LCM LCM 保持不变,则答案应当 + 1 +1 +1 。可以证明,上述贪心中被当做周期为 p p p 的数列依然只需要被当做周期为 p p p 的数列考虑,周期为 p − 1 p-1 p1 的数列依然只需要被当做周期为 p − 1 p-1 p1 的数列考虑。
  • 时间复杂度 O ( N L o g V + V ) O(NLogV+V) O(NLogV+V)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXV = 2e6 + 5;
const int P = 1e9 + 7;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n, tot, prime[MAXV], f[MAXV];
int a[MAXN], Max[MAXV], cnt[MAXV];
bool type[MAXN];
void init(int n) {
	for (int i = 2; i <= n; i++) {
		if (f[i] == 0) prime[++tot] = f[i] = i;
		for (int j = 1; j <= tot && prime[j] <= f[i]; j++) {
			int tmp = prime[j] * i;
			if (tmp > n) break;
			f[tmp] = prime[j];
		}
	}
}
int main() {
	read(n);
	for (int i = 1; i <= n; i++)
		read(a[i]);
	init(2e6);
	sort(a + 1, a + n + 1);
	reverse(a + 1, a + n + 1);
	for (int i = 1; i <= n; i++) {
		if (Max[a[i]]) {
			type[i] = true;
			int tmp = a[i] - 1;
			while (tmp != 1) {
				int fac = f[tmp], now = 0;
				while (tmp % fac == 0) {
					tmp /= fac;
					now++;
				}
				if (now > Max[fac]) {
					Max[fac] = now;
					cnt[fac] = 1;
				} else cnt[fac] += Max[fac] == now;
			}
		} else Max[a[i]] = cnt[a[i]] = 1;
	}
	int ans = 1;
	for (int i = 1; i <= 2e6; i++)
	for (int j = 1; j <= Max[i]; j++)
		ans = 1ll * ans * i % P;
	for (int i = 1; i <= n; i++) {
		bool found = false;
		if (type[i]) {
			found = true;
			int tmp = a[i] - 1;
			while (tmp != 1) {
				int fac = f[tmp], now = 0;
				while (tmp % fac == 0) {
					tmp /= fac;
					now++;
				}
				found &= Max[fac] > now || cnt[fac] >= 2;
			}
		} else found = Max[a[i]] >= 2 || cnt[a[i]] >= 2;
		if (found) {
			writeln((ans + 1) % P);
			return 0;
		}
	}
	writeln(ans);
	return 0;
}

**【E】**Euler tour

【思路要点】

  • 一个合法的欧拉序应当满足 a 1 = a 2 n − 1 a_1=a_{2n-1} a1=a2n1 ,不存在 i &lt; j &lt; k &lt; l i&lt;j&lt;k&lt;l i<j<k<l 使得 a i = a k , a j = a l , a i ≠ a j a_i=a_k,a_j=a_l,a_i\ne a_j ai=ak,aj=al,ai̸=aj ,并且若 i &lt; j , a i = a j i&lt;j,a_i=a_j i<j,ai=aj ,那么 j − i j-i ji 是偶数。
  • 对于满足上述几点的输入,我们可以用下面的算法构造出一组解。
  • 1 1 1 、若 a i = a j a_i=a_j ai=aj ,递归处理区间 ( i , j ) (i,j) (i,j) ,然后将区间 [ i , j ] [i,j] [i,j] 缩成一个点。
  • 2 2 2 、若不出现重复数的区间 ( i , j ) (i,j) (i,j) 元素个数为 2 k − 1 2k-1 2k1 ,其中非零元素个数应当不超过 k k k ,否则问题无解。将非零元素个数补足 k k k 个,则有以下两种情况:
    ( 1 ) (1) (1) 、存在三个连续的位置 0 , i , j 0,i,j 0,i,j ,将 0 0 0 赋为 j j j ,然后将这三个数看做一个 j j j
    ( 2 ) (2) (2) 、存在三个连续的位置 i , j , 0 i,j,0 i,j,0 ,将 0 0 0 赋为 i i i ,然后将这三个数看做一个 i i i
    ( 3 ) (3) (3) 、不存在上述连续位置,则序列形如 i , 0 , j , 0 , k , . . . , 0 , l i,0,j,0,k,...,0,l i,0,j,0,k,...,0,l ,在所有序列中的 0 0 0 处填入该子树的根部即可。
  • 时间复杂度 O ( N ) O(N) O(N)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int a[MAXN], nxtoccur[MAXN];
int n, pre[MAXN], suf[MAXN];
int now, vis[MAXN];
void error() {
	printf("no\n");
	exit(0);
}
int getnum() {
	while (vis[now]) now++;
	if (now > n) error();
	vis[now] = 1;
	return now;
}
void solve(int l, int r) {
	if ((r - l) % 2 != 0) error();
	int start = pre[l], endpos = suf[r];
	for (int i = suf[start]; i <= r; i = nxtoccur[i] ? nxtoccur[i] : suf[i]) {
		if (nxtoccur[i]) {
			int tmp = nxtoccur[i];
			if (tmp > r) error();
			solve(suf[i], pre[tmp]);
			int tnp = pre[i];
			suf[tnp] = tmp, pre[tmp] = tnp;
		}
	}
	int cnt = 0, all = 0;
	for (int i = suf[start]; i <= r; i = suf[i])
		all++, cnt += a[i] != 0;
	int goal = (all + 1) / 2;
	if (cnt > goal) error();
	for (int i = suf[suf[start]]; i <= r && cnt < goal; i = suf[i]) {
		if (a[i] == 0) {
			cnt++;
			a[i] = getnum();
		}
	}
	if (all != 1) {
		for (int i = suf[start], j = suf[suf[start]]; j <= r; i = suf[i], j = suf[j]) {
			while (i > suf[start] && a[i] && a[j]) {
				int tmp = pre[i];
				if (a[tmp] == 0) {
					a[tmp] = a[j];
					int tnp = pre[tmp];
					suf[tnp] = j, pre[j] = tnp;
					i = tnp;
				} else break;
			}
			while (j < r && a[i] && a[j]) {
				int tmp = suf[j];
				if (a[tmp] == 0) {
					a[tmp] = a[i];
					int tnp = suf[tmp];
					suf[i] = tnp, pre[tnp] = i;
					if (pre[i] >= l) j = i, i = pre[i];
					else j = tnp;
				} else break;
			}
		}
		for (int i = suf[start]; i <= r; i = suf[i])
			if (a[i] == 0) a[i] = a[start];
	} else if (cnt == 0) a[suf[start]] = getnum();
	endpos = pre[endpos];
	suf[start] = endpos;
	pre[endpos] = start;
}
int main() {
	read(n);
	for (int i = 1; i <= 2 * n - 1; i++) {
		read(a[i]);
		suf[i] = i + 1;
		pre[i] = i - 1;
	}
	if (a[1] && a[2 * n - 1] && a[1] != a[2 * n - 1]) error();
	if (a[1] == 0 || a[2 * n - 1] == 0) a[1] = a[2 * n - 1] = a[1] + a[2 * n - 1];
	vis[0] = 1;
	for (int i = 2 * n - 1; i >= 1; i--) {
		if (a[i]) {
			if (vis[a[i]]) nxtoccur[i] = vis[a[i]];
			vis[a[i]] = i;
		}
	}
	suf[0] = 1, pre[2 * n] = 2 * n - 1;
	solve(1, 2 * n - 1);
	printf("yes\n");
	for (int i = 1; i <= 2 * n - 1; i++)
		printf("%d ", a[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值