Codeforces 1556, Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)


终于蓝了呜呜呜
原来两场四题就能上分 以后不弃赛了

A.

注意到2, 3操作不改变总和,1操作使得总和加上了一个偶数,故直接判断总和是否为偶数即可。如果和为偶数,只要判断c和d是否相等即可。注意0要判一下

B.

先判一下奇数和偶数的个数,在按顺序分配位置就行。如果奇数和偶数个数相等,还得枚举奇数先放还是偶数先放。注意开ll。

C.

莫名其妙地ac了,不是很懂。

首先枚举左端点l和右端点r,用函数check(l,r)统计这两个端点的贡献(在l处开始,r处结束的合法括号序列的数量)。假设每一个左括号为1,右括号为-1,对序列求个前缀和,方便判断是否合法。

所有的从l开始,r结束的合法括号序列,不一定用了l和r中的每一个左右括号。

首先计算d=sum[r]-sum[l-1],以得出左右两边是否有多余的括号,我们要舍去。之后再枚举\(l+1\)\(r-1\)的每一个位置\(i\),他们必须满足\(sum[i]-sum[l-1]>=0\).统计这个差值的最小值,记为mx,其表示左边l处最多能有几个括号多出来。那么答案至多有mx+1.注意当d>0时,这个值要减去d后才能用于更新mx。

之后保险起见,还得和左边能贡献的左括号数量,右边能贡献的右括号数量取min,注意此时也要把d的偏差加上去,具体见代码。

// Problem: C. Compressed Bracket Sequence
// Contest: Codeforces - Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)
// URL: https://codeforces.com/contest/1556/problem/C
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
#define ll long long
int rd() {
    int s = 0, f = 1; char c = getchar();
    while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
    while (c >= '0' && c <= '9') {s = s * 10 + c - '0'; c = getchar();}
    return s * f;
}
int n, m, k;
ll tot;
ll c[maxn], ans, sum[maxn];
ll check(int l, int r) {
	ll d = sum[r]-sum[l-1], mx = 99999999999999999ll;
	for (int i = l+1; i < r; i += 2) {
		if (d <= 0)
			mx = min(mx, sum[i]-sum[l-1]);
		if (d > 0) 
			mx = min(mx, sum[i]-d-sum[l-1]);
	}
	if (mx < 0) return 0;
	//if (mx == 0) return 1;
	if (d == 0) return min(c[l], min(mx + 1ll, -c[r]));
	if (d < 0) return min(c[l], min(mx + 1ll, -c[r]+d));
	if (d > 0) return min(c[l]-d, min(mx+1ll, -c[r]));
}
int main() {
	n = rd();
	for (int i = 1; i <= n; i++) {
		c[i] = rd();
		if (i % 2 == 0) c[i] = -c[i];
		sum[i] = sum[i-1]+c[i];
	}
	for (int l = 1; l <= n; l += 2) {
		for (int r = l+1; r <= n; r += 2) {
			ans += check(l, r);
		}
	}
	printf("%lld\n", ans);
}

D.

\(a+b=a\&b+a|b\),用\(a+b,a+c,b+c\)可以求出\(abc\),已知\(a\)\(a\&b+a|b\)可以求\(b\),这样直接求出所有的\(a[i]\)就行。

// Problem: D. Take a Guess
// Contest: Codeforces - Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)
// URL: https://codeforces.com/contest/1556/problem/D
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 7;
#define ll long long
int rd() {
    int s = 0, f = 1; char c = getchar();
    while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
    while (c >= '0' && c <= '9') {s = s * 10 + c - '0'; c = getchar();}
    return s * f;
}
int n, m, k, tot;
ll a[maxn];
typedef pair<int, int> pii;

#define mk make_pair
ll getPlus(int i, int j) {
	printf("or %d %d\n", i, j);
	fflush(stdout);
	ll orij = rd();
	printf("and %d %d\n", i, j);
	fflush(stdout);
	ll andij = rd();
	return orij + andij;
}
void get(int i, int j, int k) {
	ll pij = getPlus(i, j);
	ll pjk = getPlus(j, k);
	ll pik = getPlus(i, k);
	a[i] = (pij - pjk + pik) / 2ll;
	a[j] = (pjk - pik + pij) / 2ll;	
	a[k] = (pik - pij + pjk) / 2ll;	
}
int main() {
	n = rd(); k = rd();
	for (int i = 1; i <= n; i += 3) {
		if (i + 2 > n) break;
		get(i, i+1, i+2);
	}
	if (n % 3 == 1) {
		ll p1 = getPlus(1, n);
		a[n] = p1 - a[1];
	}
	if (n % 3 == 2) {
		ll p1 = getPlus(1, n-1);
		a[n-1] = p1 - a[1];
		ll p2 = getPlus(1, n);
		a[n] = p2 - a[1];
	}
	sort(a+1, a+n+1);
	printf("finish %lld\n", a[k]);
	return 0;
	//fflush(stdout);
}

注意每一行输出之后要立刻fflush(stdout);

E

给出俩个长为n的整数序列a和b,以及一些询问(l,r). 对于每个\((l,r)\),回答最少要执行多少次操作,使得a[l...r]=b[l...r],或是判断无解。

每次操作为选取偶数个下标\(pos_i\),满足\(l\le pos_1 < pos_2 < \cdots <pos_k \le r\),然后让a中下标为\(pos_1,pos_3,pos_5,\cdots, pos_k-1\)的数+1,让b中下标为\(pos_2,pos_4,pos_6,\cdots,pos_k\)的位置的数+1.

每次询问是互相独立的。

sol:

每次操作不改变\(\sum_{i=l}^{r}(a[i]-b[i])[a[i]\ne b[i]]\)​​,如果初始时这个差不等于0,那么必无解。

\(c[i]=a[i]-b[i]\),操作变为令\(c[pos_{2i-1}]+=1, c[pos_{2i}]-=1\)​,有解的充分条件变为\(\sum_{i=l}^{r}c[i] =0\).

其实c题给出了这道题的提示,如果把小于0的\(c[i]\)​​看成是\(c[i]\)​​个\('('\)​​号,大于0的\(c[i]\)​​看成是\(c[i]\)​个\(')'\)​号,那么每次操作可以看成从括号序列中取出若干对形如\("()()\cdots()"\)​​的子序列删除。由于每次取出的子序列是一个合法括号序列,它们在不改变子序列内元素相对顺序的情况下任意组合,得到的一定也是一个合法括号序列(左括号数永远大于等于已经出现的右括号数,并且最终左括号数等于右括号数)。记\(\sum_{i=1}^{n}c[i]=sum[n]\)​,那么询问\((l,r)\)​有解,当且仅当\(\forall i \in [l, r], i\in \N, sum[i]-sum[l-1]\le 0\)​,并且\(sum[r]-sum[l-1]=0\)​​.即前缀和的最大值小于等于0且区间和等于0即可。不带修改的情况下查找区间最值用ST表实现。最小次数即前缀和的最小值(每一个前缀中的左括号只能归到一次操作的起点)

// Problem: E. Equilibrium
// Contest: Codeforces - Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)
// URL: https://codeforces.com/problemset/problem/1556/E
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
#define ll long long
int n, m, k, tot, a[maxn], q;
ll sum[maxn];
ll stmx[22][maxn], stmi[22][maxn];
int rd() {
	int s = 0, f = 1; char c = getchar();
	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
	while (c >= '0' && c <= '9') {s = s * 10 + c - '0'; c = getchar();}
	return s * f;
}
ll querymi(int l, int r) {
	int len = r - l + 1, len2;
	for (len2 = 0; (1 << (len2+1)) < len; len2++);
	return min(stmi[len2][l], stmi[len2][r-(1<<len2)+1]);
}
ll querymx(int l, int r) {
	int len = r - l + 1, len2;
	for (len2 = 0; (1 << (len2+1)) < len; len2++);
	return max(stmx[len2][l], stmx[len2][r-(1<<len2)+1]);
}
int main() {
	n = rd();
	q = rd();
	//printf("%d %d\n", n, q);
	for (int i = 1; i <= n; i++) 
		a[i] = rd();
	for (int i = 1; i <= n; i++) {
		a[i] -= rd();
		sum[i] = sum[i-1] + a[i];
		stmx[0][i] = stmi[0][i] = sum[i];
	}
	//q = rd();
	for (int j = 1; j <= 20; j++)
		for (int i = 1; i <= n-(1<<j)+1; i++) 
			stmi[j][i] = 100000000000001ll, stmx[j][i] = -100000000000001ll;
	for (int i = 1; i <= 20; i++) {
		for (int j = 1; j <= n - (1<<i)+1; j++) {
			stmi[i][j] = min(stmi[i-1][j], stmi[i-1][j+(1<<(i-1))]);
			stmx[i][j] = max(stmx[i-1][j], stmx[i-1][j+(1<<(i-1))]);
		}
	}
	while (q--) {
		int l = rd();
		int r = rd();
		//printf("l == %d r == %d qmx == %lld qmi == %lld\n", l, r, querymx(l, r)-sum[l-1], querymi(l, r)-sum[l-1]);
		if (sum[r]-sum[l-1]!=0 || querymx(l, r) > sum[l-1]) {
			puts("-1");
			continue;
		} else {
			printf("%lld\n", -(querymi(l, r)-sum[l-1]));
		}
	}
	return 0;
}

F

n个点,每个点有一个权值\(a_i\),任意俩点间均有边,边的方向定义如下:\(i\)与点\(j\)之间的边有\(\frac{a_i}{a_i+a_j}\)的概率由\(i\)\(j\),求图中能到达其他所有点的点数的期望。

sol:

题解 CF1556F 【Sports Betting】 - juju 的博客 - 洛谷博客 (luogu.org)

Deltix Round, Summer 2021. Editorial - Codeforces

题面中建出来的图叫竞赛图,即为无向完全图中的每一条边分配方向。“两两之间比赛总能决定出一个胜者”。

性质:一个竞赛图的强连通分量缩点之后,拓扑序唯一(缩点之后是一条链状物)。

证:考虑有向边\((u,v)\),假设\(u\),\(v\)所在的强连通分量不同,\(u\)所属的强连通分量和\(v\)所属的强连通分量(分别记为\(A,B\))之间的拓扑序一定是\(A<B\).\(A\)中其它的和\(B\)相连的边一定也是由\(A\)指向\(B\),否则它们会在同一个强连通分量中。 故他们就会变成缩点后同一条链上前后俩点(中间可能有其他点)。

一个点能到达其他所有点,说明以其为根的叶向生成树存在(缩点后这个点属于链的起点对应的强连通分量)。

枚举某个点集\(S\)作为缩点后的链上的起点,记其为起点的概率为\(f(S)\),那么\(S\)对答案的贡献为\(|S|\times f(S)\).

\(p(S,T)\)表示\(S\)中的所有与\(T\)相连的边都是自\(S\)连向\(T\)的概率,即\(S\)在链上的位置在\(T\)之前的概率,\(p\)是好求的。则\(p(S, U/S)\)表示\(S\)恰好构成SCC链上从起点开始的连续一段的概率,记为\(g(S)\)。它比\(f(S)\)多计算了\(S\)被分为大于1个SCC的概率.

多计算的概率可以通过枚举\(S\)的非空真子集\(s\)作为这条SCC链的起点,那么子集\(s\)\(f(S)\)多做的贡献为\(f(s)g(S-s)/p(S-s, s)\).

\(g(S-s)\)中多计算了\(S-s\)\(s\)间的边全为\((S-s)->s\)的概率,这个与实际相反,而\(S-s\)\(s\)间的边全为\(s->(S-s)\)的概率已经在\(f(s)\)中计算过,因此除去\(p(S-s, s)\)即可。

\[f(S)=g(S)-\sum_{s\subseteq S,s\ne \emptyset} \frac{f(s)g(S-s)}{p(S-s,s)} \]

剩下的就是状压dp了。

我大受震撼

如果暴力计算\(p\)\(g\)的话是\(O(3^n\times n^2)\)的,我自带巨大常数在第9个点TLE了。但可以选择交C++17(64)水过去。

考虑预处理出\(p\)

\[p(S,T)=\prod_{x\in S, y \in T}\frac{a[x]}{a[x]+a[y]} \]

\(H(x, T)\)表示点\(x\)与集合\(T\)中的点之间的边都是\(x->T\)的概率。则

\[p(S,T)=\prod_{x\in S}H(x, T) \]

\(H\)可以在\(O(2^n\times n^2)\)时间内预处理,这样计算\(f\)的时间就能优化到\(O(3^n\times n)\), 总复杂度降至\(O(3^nn+2^nn^2)\)

\(p\)还可以进一步优化至\(O(1)\)求。

考虑把\(n\)分成大小差不超过1的两个集合, 分别记为\(A,B\)

\(S,T\)\(A,B\)中的部分分别为\(S_A, S_B, T_A, T_B\),那么

\[p(S,T)=p(S_A,T_A)p(S_B,T_B)p(S_A,T_B)p(S_B,T_A) \]

这四部分可以分别预处理出来

要注意\(S_A,S_B,T_A,T_B\)​可能为0, 这种情况要特别处理一下,有点难调试;

// Problem: F. Sports Betting
// Contest: Codeforces - Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)
// URL: https://codeforces.com/contest/1556/problem/F
// Memory Limit: 256 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
const int maxn = 17, md = 1e9 + 7, N = (1 << 14) + 7, M = (1 << 7) + 7;
#define ll long long
int n, a[maxn], U1, U, U2, m;
ll f[N], g[N], h[maxn][N], paa[M][M], pab[M][M], pba[M][M], pbb[M][M], inv[2000007], ans;
int rd() {
	int s = 0, f = 1; char c = getchar();
	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
	while (c >= '0' && c <= '9') {s = s * 10 + c - '0'; c = getchar();}
	return s * f;
}
ll ksm(ll a, ll b) {ll res = 1; while (b) {if (b & 1) res = res * a % md; a = a * a % md; b >>= 1;} return res;}
ll P(int s, int t) {
	int sa = s & U1, ta = t & U1, sb = (s & U2) >> m, tb = (t & U2) >> m;
	return max(paa[sa][ta], 1ll)*max(pab[sa][tb], 1ll)%md*max(pba[sb][ta], 1ll)%md*max(pbb[sb][tb], 1ll)%md;
}
int main() {
	n = rd(); m = n / 2;
	U = (1 << n) - 1;  U1 = (1 << m) - 1; U2 = U ^ U1;
	for (int i = 0; i < n; i++) a[i] = rd();
	for (int i = 0; i < n; i++) 
		for (int j = 0; j < n; j++) 
			if (!inv[a[i]+a[j]]) 
				inv[a[i]+a[j]] = ksm(a[i]+a[j], md - 2);
	for (int i = 0; i < n; i++) {
		h[i][0] = 1;
		for (int S = 1; S <= U; S++) {
			if (S & (1 << i)) continue;
			h[i][S] = 1;
			for (int j = 0; j < n; j++) {
				if (S & (1 << j)) {
					h[i][S] = h[i][S] * a[i] % md * inv[a[i]+a[j]] % md;
				}
			}
		}
	}
	for (int s = 0; s <= U1; s++) {
		int us = U1 ^ s;
		paa[s][0] = 1;
		for (int t = us; t; t = (t-1) & us) {
			paa[s][t] = 1;
			for (int i = 0; i < m; i++) {
				if (s & (1 << i)) {
					paa[s][t] = paa[s][t] * h[i][t] % md;
				}
			}
		}
	}
	for (int s = 0; s <= U1; s++) {	//U1==2-1=1
		pab[s][0] = 1;
		for (int t = 0; t <= U2>>m; t++) {
			pab[s][t] = 1;
			for (int i = 0; i < m; i++) {
				if (s & (1 << i)) {
					pab[s][t] = pab[s][t] * h[i][t<<m] % md;
				}
			}
		}
	}
	for (int s = 0; s <= U2 >> m; s++) {
		pba[s][0] = 1;
		for (int t = 0; t <= U1; t++) {
			pba[s][t] = 1;
			for (int i = 0; i < n-m; i++) {
				if (s & (1<<i)) {
					pba[s][t] = pba[s][t] * h[i+m][t] % md;
				}
			}
		}
	}
	
	for (int s = 0; s <= U2 >> m; s++) {
		int us = (U2>>m)^s;
		pbb[s][0] = 1;
		for (int t = us; t; t = (t-1) & us) {
			pbb[s][t] = 1;
			for (int i = 0; i < n-m; i++) {
				if (s & (1 << i)) {
					pbb[s][t] = pbb[s][t] * h[i+m][t<<m] % md;
				}
			}
		}
	}
	for (int i = 0; i <= U; i++) g[i] = P(i, U^i);
	for (int S = 1; S <= U; S++) {
		f[S] = g[S];
		for (int s = (S-1)&S; s; s = (s-1) & S) {
			f[S] = (f[S] - f[s] * g[S-s] % md * ksm(P(S-s, s), md - 2) % md + md) % md;
		}
		int cnt = 0;
		for (int i = 0; i < n; i++) if (S & (1 << i)) cnt++;
		ans = (ans + 1ll * cnt * f[S] % md) % md;
	}
	printf("%lld\n", ans);
	return 0;
}

然后跑得比C++17 (64)的\(O(n^23^n)\)慢hhh


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM