1. 程式人生 > 其它 >Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)A~F

Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)A~F

A. A Variety of Operations

題意:只能進行三種操作:1. 對於雙方+k; 2.對於雙方一方+k,一方-k; k可以取任意數, 初始兩個數為{0,0}問最少幾步操作可以變成{n,m},不成立-1

分析:如果n和m為{0,0},那麼0步,根據題意可知,兩數和只能為偶數,所以n+m為奇數時-1,否則如果n=m,輸出1,否則輸出2

程式碼:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 2e2 + 10, mod = 1e9 + 7;

int n, m, t, k;

int s[N][N], dp[N][N], f[N];
char g[N][N];

string str;

vector<int> vec[N], vec1[N];

void solve()
{
	int a, b;
	cin >> a >> b;
	if (a == 0 && b == 0)
	{
		puts("0");
		return;
	}
	if ((a + b) % 2 == 1)
	{
		puts("-1");
		return;
	}
	if (a == b)
	{
		puts("1");
		return;
	}
	puts("2");
}

int main()
{
    t = 1;
    cin >> t;
    while(t--) 
        solve();
    return 0;
}

B. Take Your Places!

題意:多組輸入,給出一串序列,每次操作只能交換相鄰的數值,問最少交換多少次後,任意相鄰數均為一奇一偶的情況,不成立-1

分析:顯然奇數和偶數的數量不能超過1,超過不可能合法,如果奇數的數量比偶數多,那麼奇數必定會排座標奇數位置,如果偶數的數量比奇數多,那麼偶數會排在座標奇數的位置,如果相等就都有可能,顯然對於所有按位置排列的奇數,分別去對應位置會更優,比如奇數大於偶數的話,奇數按順序必定會去到1,3,5,...,n的位置,所以統計原本位置和最終位置的絕對值的和就好了,奇數偶數數量相等的時候取和的最小值

程式碼:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 3e5 + 10, mod = 1e9 + 7;

int n, m, t, k;

int s[N], dp[N], f[N];
char g[N];

string str;

vector<int> vec;

void solve()
{
	vec.clear();
	cin >> n;
	int a = 0, b = 0, c = 0, d = 0;
	for (int i = 1; i <= n; i++)
	{
		cin >> s[i];
		if (s[i] & 1)
		{
			a++;
			vec.push_back(i);
		}
		else b++;
	}
	if (abs(a - b) > 1)
	{
		puts("-1");
		return;
	}
	if (a == b)
	{
		int ans = 0, ans1 = 0;
		for (int i = 0; i < vec.size(); i++)
		{
			ans += abs(vec[i] - (2*i+1));
			ans1 += abs(vec[i] - (2*i+2));
		}
		printf("%d\n", min(ans, ans1));
		return;
	}
	if (a > b)
	{
		int ans = 0;
		for (int i = 0; i < vec.size(); i++)
			ans += abs(vec[i] - (2*i+1));
		printf("%d\n", ans);
		return;
	}
	int ans = 0;
	for (int i = 0; i < vec.size(); i++)
		ans += abs(vec[i] - (2*i+2));
	printf("%d\n", ans);
}

int main()
{
    t = 1;
    cin >> t;
    while(t--) 
        solve();
    return 0;
}

C. Compressed Bracket Sequence

題意:給出一串括號序列,以以下形式表示奇數位置是左括號的數量,偶數位置是右括號的位置,給出n個數(n<1000),每個數範圍為1到1e9,問這些括號序列的連續子串的數量

分析:因為資料範圍很小,所以暴力處理對於每個奇數位置去維護當前括號序列左括號處於該位置的情況數,對於多餘的左括號,一旦用的話,只能用一次,所以維護左括號的最小值,跟最初一減就知道過程中用過幾次,但是也可以不用多餘的括號直接拼成就好比(3,1,2,3)(3,1,2,1,1,3)對於這兩種情況,可以分成左右兩邊自成括號,也可以連線,所以用一個標記去維護左邊有沒有擋住的地方,能不能直接連到最左邊的括號上

程式碼:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 1e3 + 10, mod = 1e9 + 7;

int n, m, t, k;

ll s[N], dp[N], f[N];
char g[N];

string str;

vector<int> vec;

void solve()
{
	cin >> n;
	ll ans = 0;
	for (int i = 1; i <= n; i++)
		cin >> s[i];
	for (int i = 2; i <= n; i += 2)
	{
		ans += min(s[i], s[i-1]);
		if (s[i] > s[i-1]) s[i] -= s[i-1], s[i-1] = 0;
		else s[i-1] -= s[i], s[i] = 0;
	}
	if (n & 1) f[n] = s[n];
	else f[n] = -s[n];
	for (int i = 1; i < n; i += 2)
	{
		if (s[i+1]) continue;
		ll u = s[i], v = s[i], minn = s[i];
		ll bj = 0;
		for (int j = i + 3; j <= n; j += 2)
		{
			bj += s[j-1] - s[j];
			if (bj < 0) bj = 0;
			if (bj == 0) ans++;
			if (s[j])
			{
				u -= s[j];
				minn = min(minn, u);
				if (u < 0)
				{
					minn = 0;
					break;
				}
			}
			if (s[j-1]) u += s[j-1];
		}
		ans += v - minn;
//		cout << ans << endl;
	}
	printf("%lld\n", ans);
}

int main()
{
    t = 1;
//    cin >> t;
    while(t--) 
        solve();
    return 0;
}

D. Take a Guess

題意:互動式題,你進行不超過2n次的查詢,有兩種查詢:1.輸出or i j,會查詢到i|j;2.輸出and i 就,會查詢到i&j,最後輸出:finnish 答案;給出n和k,詢問這n個數中第k小的數

分析:2n次查詢,顯然可以有n次查詢到i|j和i&j的和而i+j=(i|j)+(i&j),而對於3個數的話,知道a+b,b+c,c+a,就可以分析出a,b,c的值了,對於後續i可以查詢第一個數和第i個數的情況,這樣就知道了n個數分別是什麼,sort排序一下就好了,對於第k小的數是可以做到O(n)複雜度的,但沒必要

程式碼:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 3e5 + 10, mod = 1e9 + 7;

int n, m, t, k;

int s[N], dp[N], f[N];
char g[N];

string str;

vector<int> vec;

void solve()
{
	int a, b, c, d;
	cin >> n >> m;
	for (int i = 2; i <= n; i++)
	{
		cout << "and " << i - 1 << ' ' << i << endl;
		cin >> a;
		cout << "or " << i - 1 << ' ' << i << endl;
		cin >> b;
		s[i] = a + b;
	}
	cout << "and " << 1 << ' ' << 3 << endl;
	cin >> a;
	cout << "or " << 1 << ' ' << 3 << endl;
	cin >> b;
	s[1] = a + b;
	a = s[1], b = s[2], c = s[3], d = (1ll * s[1] + s[2] + s[3]) / 2;
	a = d - a, b = d - b, c = d - c;
	s[1] = c, s[2] = a, s[3] = b;
	for (int i = 4; i <= n; i++) s[i] -= s[i-1];
	sort(s + 1, s + n + 1);
	cout << "finish " << s[m] << endl;
}

int main()
{
    t = 1;
//    cin >> t;
    while(t--) 
        solve();
    return 0;
}

E. Equilibrium

題意:給出長度為n的兩組數,q組查詢,每次詢問操作過後使得兩組數l到r區間相等最小運算元,可進行的操作是選定偶數個位置,對於其中奇數的位置在第一個陣列上+1,偶數的位置在第二個陣列上+1,不成立輸出-1

分析:顯然對於任意的操作,在區間內和都不會改變,所以要保證區間內兩組的和相同,否則必定不成立,對於區間內任意前i項(第二組數-第一組數)的和都一定要為非負數,如果有負數的存在意味著當處理到一定程度後,兩組數不相等的第一個數的位置,第一組的數值大於第二組的數值,講無論如何也沒辦法找到合法解,所以問題就轉變成了,[l,r]區間內,dp[r]==dp[l-1] $ $ (min\(\sum_{i=l}^{r}\)dp[i]) == dp[r],如果不成立-1,否則因為各個地方可以不矛盾的選擇,所以只要選最大值-最小值就是答案$ $ (max\(\sum_{i=l}^{r}\)dp[i])-$ $ (min\(\sum_{i=l}^{r}\)dp[i]),區間定值查詢最大最小值可以用st表,時間複雜度:O(nlogn)

程式碼:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>
#include <cmath>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 3e5 + 10, mod = 1e9 + 7;

int n, m, t, k;

ll s[N], dp[N], f[N], g[N][20], g1[N][20];

string str;

vector<int> vec;

void init()
{
	for (int i = 1; i <= n; i++) g[i][0] = dp[i];
	for (int j = 1; j <= 20; j++)
		for (int i = 1; i <= n; i++)
			if (i + (1 << j) - 1 <= n)
				g[i][j] = max(g[i][j-1], g[i+(1<<(j-1))][j-1]);
	
	for (int i = 1; i <= n; i++) g1[i][0] = dp[i];
	for (int j = 1; j <= 20; j++)
		for (int i = 1; i + (1 << j) - 1 <= n; i++)	
			g1[i][j] = min(g1[i][j-1], g1[i+(1<<(j-1))][j-1]);
}

ll querymax(int l, int r)
{
	int len = log(r - l + 1) / log(2);
	return max(g[l][len], g[r-(1<<len)+1][len]);
}

ll querymin(int l, int r)
{
	int len = log(r - l + 1) / log(2);
	return min(g1[l][len], g1[r-(1<<len)+1][len]);
}

void solve()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++) cin >> s[i];
	for (int i = 1; i <= n; i++) cin >> f[i];
	for (int i = 1; i <= n; i++) dp[i] = dp[i-1] + f[i] - s[i];
	init();
	while (m--)
	{
		int a, b;
		cin >> a >> b;
		if (querymin(a,b) == dp[a-1] && dp[a-1] == dp[b])
			cout << querymax(a,b) - querymin(a,b) << endl;
		else puts("-1"); 
	}
}

int main()
{
    t = 1;
//    cin >> t;
    while(t--) 
        solve();
    return 0;
}

F. Sports Betting

題意:給出n個人(n<=14),每個人的值為a[i],i與j打,i贏的概率為\(\frac{a[i]}{a[i]+a[j]}\),不存在平局,輸者淘汰制,一個人直接或間接打敗其餘所有人算勝者,問勝者的期望概率,取餘1e9+7

分析:看的排行榜大佬的程式碼,思路巧妙。首先預處理第i個人打敗第j個人的概率,有\(2^{n}\)種狀態,在狀態i下,第j個人贏狀態i下所有人的概率,然後開始計算,顯然每個人都可能成為勝者,那就分別計算每個人i成為勝者的期望概率加起來就好了,初始化g[1<<i]=1,自己一個人肯定百分之百是勝者,然後列舉從小到大每種狀態,如果狀態i為i本身或者沒有i本身,沒意義跳過,跳過之後,我們計算所有失敗情況的概率,一減就是勝利情況的概率了,這裡枚舉了狀態j的所有情況,保留了其中所有包括i的狀態,然後用最初1的概率迴圈乘dp[u][s],s這個人不在狀態u內,在狀態j內,並且打敗了u狀態每一人的概率(我最初想,如果是失敗的概率的話,那麼狀態u被u以外的的任意一個s打敗不都輸了麼,成功的概率不應該是所有符合條件的s的\(\prod\)(1-dp[u][s]),怎麼會是\(1 - \prod\)dp[u][s]呢,但事實上這裡考慮了容斥,對於非全勝的情況可以向上轉化,比如在算111101的時候,狀態111000,這裡有四種情況000000,000100,000001,000101,第一種失敗的不算,第四種算,第二三種可以分別向上轉化為不重複,111000+000100嚴格等價於111001+000100,所以這裡只討論跟狀態u以外的全敗即可,之前的狀態均為已知)時間複雜度非常高n*\(\sum_{i= 0}^{1<<n}*3^{len(i)}*n\),len(i)為二進位制狀態下1的個數,估算時間複雜度極高,過程中少量剪枝後,我走了遍n為14的資料,發現運行了141477994,接近1.5e8了,(題目時限4s)

程式碼:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 5 + 10, mod = 1e9 + 7;

int n, m, t, k;

int s[N], dp[1 << N][N], f[N][N], g[1 << N];

string str;

vector<int> vec;

int ksm(int a, int b, int p)
{
	int res = 1;
	while (b)
	{
		if (b & 1) res = 1ll * res * a % p;
		a = 1ll * a * a % p;
		b >>= 1;
	}
	return res;
}

void solve()
{
	cin >> n;
	for (int i = 0; i < n; i++) cin >> s[i];
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
		 	f[i][j] = 1ll * s[i] * ksm((s[i] + s[j]) % mod, mod - 2, mod) % mod;
	
	for (int i = 0; i < (1 << n); i++)
		for (int j = 0; j < n; j++)
		{
			dp[i][j] = 1;
			for (int k = 0; k < n; k++)
				if (i & (1 << k))
					dp[i][j] = 1ll * dp[i][j] * f[j][k] % mod;
		}
	
	ll ans = 0;
	for (int i = 0; i < n; i++)
	{
		memset(g, 0, sizeof g);
		g[1 << i] = 1;
		for (int j = 0; j < (1 << n); j++)
		{
			if (!(j & (1 << i))) continue;
			if (j == (1 << i)) continue;
			g[j] = 1;
			for (int u = j; u; u = (u - 1) & j)
			{
				if (!(u & (1 << i))) continue;
				if (u == j) continue;
				ll cur = g[u];
				for (int s = 0; s < n; s++)
					if ((u ^ j) & (1 << s))
						cur = cur * dp[u][s] % mod;
				g[j] = (g[j] - cur) % mod;
			}
		}
		ans += g[(1 << n) - 1];
		ans %= mod;
	}
	if (ans < 0) ans += mod;
	cout << ans << endl;
}

int main()
{
    t = 1;
//    cin >> t;
    while(t--) 
        solve();
    return 0;
}