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